Compare commits

..

1 Commits

Author SHA1 Message Date
Álison Fernandes
225cb24ac1 Add ViewAsQrCode first draft 2025-03-19 15:15:26 +00:00
1639 changed files with 101654 additions and 111188 deletions

2
.github/CODEOWNERS vendored
View File

@@ -5,7 +5,7 @@
# 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
* @bitwarden/team-android @brian-livefront @david-livefront @dseverns-livefront @ahaisting-livefront @phil-livefront
# Actions and workflow changes.
.github/ @bitwarden/dept-development-mobile

View File

@@ -1 +0,0 @@
3.13

View File

@@ -1,39 +0,0 @@
# 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

@@ -1,20 +0,0 @@
{
"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

@@ -1,48 +0,0 @@
{
"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

@@ -1,20 +0,0 @@
{
"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

@@ -1,48 +0,0 @@
#!/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

@@ -1,145 +0,0 @@
#!/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

@@ -39,10 +39,10 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
~/.gradle/caches
@@ -52,7 +52,7 @@ jobs:
${{ runner.os }}-gradle-v2-
- name: Cache build output
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
${{ github.workspace }}/build-cache
@@ -61,13 +61,13 @@ jobs:
${{ runner.os }}-build-
- name: Configure JDK
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
- name: Configure Ruby
uses: ruby/setup-ruby@ca041f971d66735f3e5ff1e21cc13e2d51e7e535 # v1.233.0
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
@@ -78,7 +78,7 @@ jobs:
bundle install --jobs 4 --retry 3
- name: Check Authenticator
run: bundle exec fastlane check
run: bundle exec fastlane checkAuthenticator
- name: Build Authenticator
run: bundle exec fastlane buildAuthenticatorDebug
@@ -98,7 +98,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Configure Ruby
uses: ruby/setup-ruby@ca041f971d66735f3e5ff1e21cc13e2d51e7e535 # v1.233.0
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
@@ -162,10 +162,10 @@ jobs:
json_key:${{ github.workspace }}/secrets/authenticator_play_store-creds.json }}
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
~/.gradle/caches
@@ -175,7 +175,7 @@ jobs:
${{ runner.os }}-gradle-v2-
- name: Cache build output
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
${{ github.workspace }}/build-cache
@@ -184,7 +184,7 @@ jobs:
${{ runner.os }}-build-
- name: Configure JDK
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
@@ -224,7 +224,7 @@ jobs:
- name: Upload release Play Store .aab artifact
if: ${{ matrix.variant == 'aab' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.bitwarden.authenticator.aab
path: authenticator/build/outputs/bundle/release/com.bitwarden.authenticator.aab
@@ -232,7 +232,7 @@ jobs:
- name: Upload release .apk artifact
if: ${{ matrix.variant == 'apk' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.bitwarden.authenticator.apk
path: authenticator/build/outputs/apk/release/com.bitwarden.authenticator.apk
@@ -252,7 +252,7 @@ jobs:
- name: Upload .apk SHA file for release
if: ${{ matrix.variant == 'apk' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: authenticator-android-apk-sha256.txt
path: ./authenticator-android-apk-sha256.txt
@@ -260,7 +260,7 @@ jobs:
- name: Upload .aab SHA file for release
if: ${{ matrix.variant == 'aab' }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: authenticator-android-aab-sha256.txt
path: ./authenticator-android-aab-sha256.txt

View File

@@ -40,10 +40,10 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
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@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
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@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
- name: Configure Ruby
uses: ruby/setup-ruby@ca041f971d66735f3e5ff1e21cc13e2d51e7e535 # v1.233.0
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
@@ -85,7 +85,7 @@ jobs:
run: bundle exec fastlane assembleDebugApks
- name: Upload test reports on failure
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
if: failure()
with:
name: test-reports
@@ -106,7 +106,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Configure Ruby
uses: ruby/setup-ruby@ca041f971d66735f3e5ff1e21cc13e2d51e7e535 # v1.233.0
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
@@ -157,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@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
~/.gradle/caches
@@ -170,7 +170,7 @@ jobs:
${{ runner.os }}-gradle-v2-
- name: Cache build output
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
${{ github.workspace }}/build-cache
@@ -179,7 +179,7 @@ jobs:
${{ runner.os }}-build-
- name: Configure JDK
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
@@ -253,7 +253,7 @@ jobs:
- name: Upload release Play Store .aab artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'aab') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.aab
path: app/build/outputs/bundle/standardRelease/com.x8bit.bitwarden.aab
@@ -261,7 +261,7 @@ jobs:
- name: Upload beta Play Store .aab artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'aab') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.beta.aab
path: app/build/outputs/bundle/standardBeta/com.x8bit.bitwarden.beta.aab
@@ -269,7 +269,7 @@ jobs:
- name: Upload release .apk artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.apk
path: app/build/outputs/apk/standard/release/com.x8bit.bitwarden.apk
@@ -277,7 +277,7 @@ jobs:
- name: Upload beta .apk artifact
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
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.beta.apk
@@ -286,7 +286,7 @@ jobs:
# When building variants other than 'prod'
- name: Upload debug .apk artifact
if: ${{ (matrix.variant != 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
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.dev.apk
@@ -324,7 +324,7 @@ jobs:
- name: Upload .apk SHA file for release
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.apk-sha256.txt
path: ./com.x8bit.bitwarden.apk-sha256.txt
@@ -332,7 +332,7 @@ jobs:
- name: Upload .apk SHA file for beta
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
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
@@ -340,7 +340,7 @@ jobs:
- name: Upload .aab SHA file for release
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'aab') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
with:
name: com.x8bit.bitwarden.aab-sha256.txt
path: ./com.x8bit.bitwarden.aab-sha256.txt
@@ -348,7 +348,7 @@ jobs:
- name: Upload .aab SHA file for beta
if: ${{ (matrix.variant == 'prod') && (matrix.artifact == 'aab') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
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
@@ -356,7 +356,7 @@ jobs:
- name: Upload .apk SHA file for debug
if: ${{ (matrix.variant != 'prod') && (matrix.artifact == 'apk') }}
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
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
@@ -405,7 +405,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Configure Ruby
uses: ruby/setup-ruby@ca041f971d66735f3e5ff1e21cc13e2d51e7e535 # v1.233.0
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
@@ -442,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@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
~/.gradle/caches
@@ -455,7 +455,7 @@ jobs:
${{ runner.os }}-gradle-v2-
- name: Cache build output
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
${{ github.workspace }}/build-cache
@@ -464,7 +464,7 @@ jobs:
${{ runner.os }}-build-
- name: Configure JDK
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
@@ -515,7 +515,7 @@ jobs:
keyPassword:"${{ env.FDROID_BETA_KEY_PASSWORD }}"
- name: Upload F-Droid .apk artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
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.apk
@@ -527,14 +527,14 @@ jobs:
> ./com.x8bit.bitwarden-fdroid.apk-sha256.txt
- name: Upload F-Droid SHA file
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
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@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
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.beta-fdroid.apk
@@ -546,7 +546,7 @@ jobs:
> ./com.x8bit.bitwarden.beta-fdroid.apk-sha256.txt
- name: Upload F-Droid Beta SHA file
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
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

View File

@@ -1,98 +0,0 @@
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

@@ -29,14 +29,14 @@ jobs:
secrets: "github-gpg-private-key, github-gpg-private-key-passphrase"
- name: Generate GH App token
uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2
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@b8012bd5491b8aa8578b73ab5b5f5e7c94aaa6e2 # v2.7.0
uses: crowdin/github-action@d1632879d4d4da358f2d040f79fa094571c9a649 # v2.5.1
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }}

View File

@@ -29,14 +29,14 @@ jobs:
secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase"
- name: Generate GH App token
uses: actions/create-github-app-token@3ff1caaa28b64c9cc276ce0a02e2ff584f3900c5 # v2.0.2
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@b8012bd5491b8aa8578b73ab5b5f5e7c94aaa6e2 # v2.7.0
uses: crowdin/github-action@d1632879d4d4da358f2d040f79fa094571c9a649 # v2.5.1
env:
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }}

View File

@@ -20,7 +20,7 @@ jobs:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Upload sources
uses: crowdin/github-action@b8012bd5491b8aa8578b73ab5b5f5e7c94aaa6e2 # v2.7.0
uses: crowdin/github-action@d1632879d4d4da358f2d040f79fa094571c9a649 # v2.5.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
CROWDIN_API_TOKEN: ${{ secrets.CROWDIN_API_TOKEN }}

View File

@@ -29,7 +29,7 @@ jobs:
secrets: "crowdin-api-token"
- name: Upload sources
uses: crowdin/github-action@b8012bd5491b8aa8578b73ab5b5f5e7c94aaa6e2 # v2.7.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 }}

View File

@@ -95,7 +95,7 @@ jobs:
- name: Create Release
id: create_release
uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631 # v2.2.2
uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
with:
tag_name: "v${{ inputs.version-name }}"
name: "${{ inputs.version-name }} (${{ inputs.version-number }})"

View File

@@ -0,0 +1,76 @@
name: Scan Authenticator
on:
workflow_dispatch:
push:
branches:
- "main"
- "rc"
- "hotfix-rc"
pull_request_target:
types: [opened, synchronize]
jobs:
check-run:
name: Check PR run
uses: bitwarden/gh-actions/.github/workflows/check-run.yml@main
sast:
name: SAST scan
runs-on: ubuntu-24.04
needs: check-run
permissions:
contents: read
pull-requests: write
security-events: write
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Scan with Checkmarx
uses: checkmarx/ast-github-action@184bf2f64f55d1c93fd6636d539edf274703e434 # 2.0.41
env:
INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}"
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 . ${{ env.INCREMENTAL }}
- name: Upload Checkmarx results to GitHub
uses: github/codeql-action/upload-sarif@d68b2d4edb4189fd2a5366ac14e72027bd4b37dd # v3.28.2
with:
sarif_file: cx_result.sarif
quality:
name: Quality scan
runs-on: ubuntu-24.04
needs: check-run
permissions:
contents: read
pull-requests: write
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
- 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 }}
-Dsonar.pullrequest.key=${{ github.event.pull_request.number }}

View File

@@ -21,7 +21,7 @@ jobs:
fetch-depth: 0
- name: Scan with Checkmarx
uses: checkmarx/ast-github-action@ef93013c95adc60160bc22060875e90800d3ecfc # 2.3.19
uses: checkmarx/ast-github-action@184bf2f64f55d1c93fd6636d539edf274703e434 # 2.0.41
with:
project_name: ${{ github.repository }}
cx_tenant: ${{ secrets.CHECKMARX_TENANT }}
@@ -34,7 +34,7 @@ jobs:
--output-path .
- name: Upload Checkmarx results to GitHub
uses: github/codeql-action/upload-sarif@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15
uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
with:
sarif_file: cx_result.sarif
@@ -51,10 +51,11 @@ jobs:
fetch-depth: 0
- name: Scan with SonarCloud
uses: sonarsource/sonarqube-scan-action@aa494459d7c39c106cc77b166de8b4250a32bb97 # v5.1.0
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 }}
-Dsonar.pullrequest.key=${{ github.event.pull_request.number }}

View File

@@ -2,14 +2,8 @@ name: Scan Pull Requests
on:
workflow_dispatch:
pull_request:
types: [opened, synchronize, reopened]
branches-ignore:
- main
pull_request_target:
types: [opened, synchronize, reopened]
branches:
- main
types: [opened, synchronize]
jobs:
check-run:
@@ -32,7 +26,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Scan with Checkmarx
uses: checkmarx/ast-github-action@ef93013c95adc60160bc22060875e90800d3ecfc # 2.3.19
uses: checkmarx/ast-github-action@184bf2f64f55d1c93fd6636d539edf274703e434 # 2.0.41
env:
INCREMENTAL: "${{ contains(github.event_name, 'pull_request') && '--sast-incremental' || '' }}"
with:
@@ -47,11 +41,9 @@ jobs:
--output-path . ${{ env.INCREMENTAL }}
- name: Upload Checkmarx results to GitHub
uses: github/codeql-action/upload-sarif@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15
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
@@ -69,7 +61,7 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}
- name: Scan with SonarCloud
uses: sonarsource/sonarqube-scan-action@aa494459d7c39c106cc77b166de8b4250a32bb97 # v5.1.0
uses: sonarsource/sonarqube-scan-action@bfd4e558cda28cda6b5defafb9232d191be8c203 # v4.2.1
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:

View File

@@ -0,0 +1,82 @@
name: Test Authenticator
on:
push:
branches:
- "main"
- "rc"
- "hotfix-rc"
pull_request_target:
types: [opened, synchronize]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
JAVA_VERSION: 17
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
packages: read
pull-requests: write
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ github.event.pull_request.head.sha }}
- 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 Ruby
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
- name: Configure JDK
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env.JAVA_VERSION }}
- name: Install Fastlane
run: |
gem install bundler:2.2.27
bundle config path vendor/bundle
bundle install --jobs 4 --retry 3
- name: Build and test Authenticator
run: |
bundle exec fastlane checkAuthenticator
- name: Upload to codecov.io
uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5.1.2
with:
files: authenticator/build/reports/kover/reportDebug.xml

View File

@@ -23,17 +23,16 @@ jobs:
runs-on: ubuntu-24.04
permissions:
packages: read
pull-requests: write
steps:
- name: Check out repo
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Validate Gradle wrapper
uses: gradle/actions/wrapper-validation@06832c7b30a0129d7fb559bcc6e43d26f6374244 # v4.3.1
uses: gradle/actions/wrapper-validation@0bdd871935719febd78681f197cd39af5b6e16a6 # v4.2.2
- name: Cache Gradle files
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
~/.gradle/caches
@@ -43,7 +42,7 @@ jobs:
${{ runner.os }}-gradle-v2-
- name: Cache build output
uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3
uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: |
${{ github.workspace }}/build-cache
@@ -52,12 +51,12 @@ jobs:
${{ runner.os }}-build-
- name: Configure Ruby
uses: ruby/setup-ruby@ca041f971d66735f3e5ff1e21cc13e2d51e7e535 # v1.233.0
uses: ruby/setup-ruby@28c4deda893d5a96a6b2d958c5b47fc18d65c9d3 # v1.213.0
with:
bundler-cache: true
- name: Configure JDK
uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # v4.7.1
uses: actions/setup-java@7a6d8a8234af8eb26422e24e3006232cccaa061b # v4.6.0
with:
distribution: "temurin"
java-version: ${{ env._JAVA_VERSION }}
@@ -75,30 +74,40 @@ jobs:
bundle exec fastlane check
- name: Upload test reports
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0
if: always()
with:
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/
app/build/reports/kover/reportStandardDebug.xml
report:
name: Process Test Reports
needs: test
runs-on: ubuntu-24.04
permissions:
contents: read
issues: write
pull-requests: write
if: success()
steps:
- name: Download test artifacts
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
if: github.event_name == 'push' || github.event_name == 'pull_request'
with:
name: test-reports
- name: Upload to codecov.io
id: upload-to-codecov
uses: codecov/codecov-action@ad3126e916f78f00edff4ed0317cf185271ccc2d # v5.4.2
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
files: kover/reportStandardDebug.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')

7
.gitignore vendored
View File

@@ -28,10 +28,3 @@ user.properties
/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

View File

@@ -10,18 +10,17 @@ GEM
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.2)
aws-partitions (1.1102.0)
aws-sdk-core (3.223.0)
aws-partitions (1.1067.0)
aws-sdk-core (3.220.1)
aws-eventstream (~> 1, >= 1.3.0)
aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
base64
jmespath (~> 1, >= 1.6.1)
logger
aws-sdk-kms (1.100.0)
aws-sdk-kms (1.99.0)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sigv4 (~> 1.5)
aws-sdk-s3 (1.185.0)
aws-sdk-s3 (1.182.0)
aws-sdk-core (~> 3, >= 3.216.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
@@ -71,7 +70,7 @@ GEM
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.4.0)
fastlane (2.227.2)
fastlane (2.227.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -111,9 +110,9 @@ GEM
tty-spinner (>= 0.8.0, < 1.0.0)
word_wrap (~> 1.0.0)
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.4.1)
xcpretty (~> 0.4.0)
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-plugin-firebase_app_distribution (0.10.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)
@@ -165,10 +164,9 @@ GEM
httpclient (2.9.0)
mutex_m
jmespath (1.6.2)
json (2.11.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)
@@ -180,7 +178,7 @@ GEM
optparse (0.6.0)
os (1.1.4)
plist (3.7.2)
public_suffix (6.0.2)
public_suffix (6.0.1)
rake (13.2.1)
representable (3.2.0)
declarative (< 0.1.0)
@@ -192,7 +190,7 @@ GEM
ruby2_keywords (0.0.5)
rubyzip (2.4.1)
security (0.1.5)
signet (0.20.0)
signet (0.19.0)
addressable (~> 2.8)
faraday (>= 0.17.5, < 3.a)
jwt (>= 1.5, < 3.0)
@@ -221,7 +219,7 @@ GEM
colored2 (~> 3.1)
nanaimo (~> 0.4.0)
rexml (>= 3.3.6, < 4.0)
xcpretty (0.4.1)
xcpretty (0.4.0)
rouge (~> 3.28.0)
xcpretty-travis-formatter (1.0.1)
xcpretty (~> 0.2, >= 0.0.7)
@@ -238,4 +236,4 @@ RUBY VERSION
ruby 3.3.1p55
BUNDLED WITH
2.6.6
2.5.9

View File

@@ -4,7 +4,6 @@
- [Compatibility](#compatibility)
- [Setup](#setup)
- [Theme](#theme)
- [Dependencies](#dependencies)
## Compatibility
@@ -16,6 +15,7 @@
## Setup
1. Clone the repository:
```sh
@@ -52,36 +52,12 @@
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.
## Theme
### Icons & Illustrations
The app supports light mode, dark mode and dynamic colors. Most icons in the app will display correctly using tinting but multi-tonal icons and illustrations require extra processing in order to be displayed properly with dynamic colors.
All illustrations and multi-tonal icons require the svg paths to be tagged with the `name` attribute in order for each individual path to be tinted the appropriate color. Any untagged path will not be tinted and the resulting image will be incorrect.
The supported tags are as follows:
* outline
* primary
* secondary
* tertiary
* accent
* logo
* navigation
* navigationActiveAccent
## Dependencies
### Application Dependencies
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.
@@ -102,7 +78,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 Camera**
- **AndroidX CameraX Camera2**
- https://developer.android.com/jetpack/androidx/releases/camera
- Purpose: Display and capture images for barcode scanning.
- License: Apache 2.0
@@ -112,9 +88,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**
- **AndroidX Core SplashScreen**
- https://developer.android.com/jetpack/androidx/releases/core
- Purpose: Backwards compatible platform features and APIs.
- Purpose: Backwards compatible SplashScreen API implementation.
- License: Apache 2.0
- **AndroidX Credentials**
@@ -127,11 +103,6 @@ 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.
@@ -152,6 +123,21 @@ 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
- **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
- **Glide**
- https://github.com/bumptech/glide
- Purpose: Image loading and caching.
@@ -172,6 +158,11 @@ 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.
@@ -187,28 +178,16 @@ The following is a list of all third-party dependencies included as part of the
- Purpose: Extensible logging library for Android.
- License: Apache 2.0
- **zxcvbn4j**
- https://github.com/nulab/zxcvbn4j
- Purpose: Password strength estimation.
- License: MIT
- **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

@@ -13,13 +13,16 @@ 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)
}
/**
@@ -51,7 +54,7 @@ android {
minSdk = libs.versions.minSdk.get().toInt()
targetSdk = libs.versions.targetSdk.get().toInt()
versionCode = 1
versionName = "2025.4.0"
versionName = "2024.9.0"
setProperty("archivesBaseName", "com.x8bit.bitwarden")
@@ -99,7 +102,6 @@ android {
applicationIdSuffix = ".beta"
isDebuggable = false
isMinifyEnabled = true
matchingFallbacks += listOf("release")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
@@ -211,12 +213,7 @@ dependencies {
add("standardImplementation", dependencyNotation)
}
implementation(files("libs/authenticatorbridge-1.0.1-release.aar"))
implementation(project(":core"))
implementation(project(":data"))
implementation(project(":network"))
implementation(project(":ui"))
implementation(files("libs/authenticatorbridge-1.0.0-release.aar"))
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)
@@ -229,7 +226,6 @@ 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)
@@ -254,6 +250,7 @@ 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))
@@ -272,15 +269,8 @@ dependencies {
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(testFixtures(project(":ui")))
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)
@@ -288,9 +278,90 @@ 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*",
"*_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")
}
getByName("sonar") {
dependsOn("check")
}
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"
@@ -310,6 +381,18 @@ afterEvaluate {
.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()) {

Binary file not shown.

View File

@@ -1,9 +1,9 @@
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 com.x8bit.bitwarden.data.platform.repository.model.Environment
import timber.log.Timber
/**

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" />
<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.
@@ -76,15 +76,16 @@
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="https" />
<data android:host="*.bitwarden.com" />
<data android:host="*.bitwarden.eu" />
<data android:host="vault.bitwarden.com" />
<data android:host="vault.bitwarden.eu" />
<data android:host="*.bitwarden.pw" />
<data android:pathPattern="/redirect-connector.*" />
</intent-filter>
<intent-filter>
<action android:name="com.x8bit.bitwarden.credentials.ACTION_CREATE_PASSKEY" />
<action android:name="com.x8bit.bitwarden.credentials.ACTION_GET_PASSKEY" />
<action android:name="com.x8bit.bitwarden.credentials.ACTION_UNLOCK_ACCOUNT" />
<action android:name="com.x8bit.bitwarden.fido2.ACTION_CREATE_PASSKEY" />
<action android:name="com.x8bit.bitwarden.fido2.ACTION_GET_PASSKEY" />
<action android:name="com.x8bit.bitwarden.fido2.ACTION_UNLOCK_ACCOUNT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
@@ -309,14 +310,6 @@
android:exported="true"
android:permission="${applicationId}.permission.AUTHENTICATOR_BRIDGE_SERVICE" />
<!-- Firebase SDK initOrder is 100. We use a higher order to initialize first -->
<provider
android:name=".data.platform.contentprovider.UncaughtErrorLoggingContentProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="false"
android:initOrder="101" />
</application>
<queries>

View File

@@ -12,6 +12,20 @@
]
}
},
{
"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"
}
]
}
},
{
"type": "android",
"info": {
@@ -48,6 +62,18 @@
]
}
},
{
"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": {

View File

@@ -160,78 +160,6 @@
]
}
},
{
"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": {
@@ -643,142 +571,6 @@
}
]
}
},
{
"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.bitwarden.core.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.platform.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.bitwarden.core.annotation.OmitFromCoverage
import com.x8bit.bitwarden.data.platform.annotation.OmitFromCoverage
import dagger.hilt.android.AndroidEntryPoint
/**

View File

@@ -1,13 +1,13 @@
package com.x8bit.bitwarden
import android.content.Intent
import com.bitwarden.ui.platform.base.BaseViewModel
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.auth.repository.util.getCaptchaCallbackTokenResult
import com.x8bit.bitwarden.data.auth.repository.util.getDuoCallbackTokenResult
import com.x8bit.bitwarden.data.auth.repository.util.getSsoCallbackResult
import com.x8bit.bitwarden.data.auth.repository.util.getWebAuthResultOrNull
import com.x8bit.bitwarden.data.auth.util.getYubiKeyResultOrNull
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject

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

@@ -2,7 +2,6 @@ package com.x8bit.bitwarden
import android.content.Intent
import androidx.lifecycle.viewModelScope
import com.bitwarden.ui.platform.base.BaseViewModel
import com.bitwarden.vault.CipherView
import com.x8bit.bitwarden.data.auth.repository.AuthRepository
import com.x8bit.bitwarden.data.autofill.util.getTotpCopyIntentOrNull
@@ -10,6 +9,7 @@ import com.x8bit.bitwarden.data.platform.util.launchWithTimeout
import com.x8bit.bitwarden.data.vault.repository.VaultRepository
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
import com.x8bit.bitwarden.data.vault.repository.util.statusFor
import com.x8bit.bitwarden.ui.platform.base.BaseViewModel
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.mapNotNull

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.credentials.BitwardenCredentialProviderService
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
@@ -30,7 +30,7 @@ class BitwardenAppComponentFactory : AppComponentFactory() {
* * [BitwardenAccessibilityService]
* * [BitwardenAutofillService]
* * [BitwardenAutofillTileService]
* * [BitwardenCredentialProviderService]
* * [BitwardenFido2ProviderService]
* * [BitwardenVaultTileService]
* * [BitwardenGeneratorTileService]
*/
@@ -63,7 +63,7 @@ class BitwardenAppComponentFactory : AppComponentFactory() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
super.instantiateServiceCompat(
cl,
BitwardenCredentialProviderService::class.java.name,
BitwardenFido2ProviderService::class.java.name,
intent,
)
} else {

View File

@@ -1,15 +1,13 @@
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.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.network.NetworkConnectionManager
import com.x8bit.bitwarden.data.platform.manager.restriction.RestrictionManager
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber
import javax.inject.Inject
/**
@@ -23,9 +21,6 @@ class BitwardenApplication : Application() {
@Inject
lateinit var logsManager: LogsManager
@Inject
lateinit var networkConnectionManager: NetworkConnectionManager
@Inject
lateinit var networkConfigManager: NetworkConfigManager
@@ -37,9 +32,4 @@ class BitwardenApplication : Application() {
@Inject
lateinit var restrictionManager: RestrictionManager
override fun onLowMemory() {
super.onLowMemory()
Timber.w("onLowMemory")
}
}

View File

@@ -17,22 +17,21 @@ import androidx.core.os.LocaleListCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.compose.NavHost
import com.bitwarden.core.annotation.OmitFromCoverage
import com.bitwarden.ui.platform.base.util.EventsEffect
import com.bitwarden.ui.platform.theme.BitwardenTheme
import androidx.navigation.compose.rememberNavController
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.components.util.rememberBitwardenNavController
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.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
@@ -82,7 +81,7 @@ class MainActivity : AppCompatActivity() {
AppCompatDelegate.setDefaultNightMode(settingsRepository.appTheme.osValue)
setContent {
val state by mainViewModel.stateFlow.collectAsStateWithLifecycle()
val navController = rememberBitwardenNavController(name = "MainActivity")
val navController = rememberNavController()
EventsEffect(viewModel = mainViewModel) { event ->
when (event) {
is MainEvent.CompleteAccessibilityAutofill -> {
@@ -124,10 +123,7 @@ class MainActivity : AppCompatActivity() {
}
},
)
BitwardenTheme(
theme = state.theme,
dynamicColor = state.isDynamicColorsEnabled,
) {
BitwardenTheme(theme = state.theme) {
NavHost(
navController = navController,
startDestination = ROOT_ROUTE,
@@ -175,11 +171,9 @@ class MainActivity : AppCompatActivity() {
settingsRepository.appLanguage
}
mainViewModel.trySendAction(
action = MainAction.AppSpecificLanguageUpdate(
appLanguage = appSpecificLanguage ?: AppLanguage.DEFAULT,
),
)
appSpecificLanguage?.let {
mainViewModel.trySendAction(MainAction.AppSpecificLanguageUpdate(it))
}
}
override fun onStop() {

View File

@@ -4,10 +4,6 @@ import android.content.Intent
import android.os.Parcelable
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.bitwarden.ui.platform.base.BaseViewModel
import com.bitwarden.ui.platform.feature.settings.appearance.model.AppTheme
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
@@ -15,13 +11,13 @@ import com.x8bit.bitwarden.data.auth.repository.model.EmailTokenResult
import com.x8bit.bitwarden.data.auth.util.getCompleteRegistrationDataIntentOrNull
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.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.credentials.manager.BitwardenCredentialManager
import com.x8bit.bitwarden.data.credentials.util.getCreateCredentialRequestOrNull
import com.x8bit.bitwarden.data.credentials.util.getFido2AssertionRequestOrNull
import com.x8bit.bitwarden.data.credentials.util.getGetCredentialsRequestOrNull
import com.x8bit.bitwarden.data.platform.manager.AppResumeManager
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.platform.manager.SpecialCircumstanceManager
@@ -35,7 +31,11 @@ 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
@@ -72,7 +72,7 @@ class MainViewModel @Inject constructor(
private val addTotpItemFromAuthenticatorManager: AddTotpItemFromAuthenticatorManager,
private val specialCircumstanceManager: SpecialCircumstanceManager,
private val garbageCollectionManager: GarbageCollectionManager,
private val bitwardenCredentialManager: BitwardenCredentialManager,
private val fido2CredentialManager: Fido2CredentialManager,
private val intentManager: IntentManager,
private val settingsRepository: SettingsRepository,
private val vaultRepository: VaultRepository,
@@ -88,7 +88,6 @@ class MainViewModel @Inject constructor(
isErrorReportingDialogEnabled = featureFlagManager.getFeatureFlag(
key = FlagKey.MobileErrorReporting,
),
isDynamicColorsEnabled = settingsRepository.isDynamicColorsEnabled,
),
) {
private var specialCircumstance: SpecialCircumstance?
@@ -139,12 +138,6 @@ class MainViewModel @Inject constructor(
.onEach(::trySendAction)
.launchIn(viewModelScope)
settingsRepository
.isDynamicColorsEnabledFlow
.map { MainAction.Internal.DynamicColorsUpdate(it) }
.onEach(::trySendAction)
.launchIn(viewModelScope)
authRepository
.userStateFlow
.drop(count = 1)
@@ -216,7 +209,6 @@ class MainViewModel @Inject constructor(
is MainAction.Internal.ScreenCaptureUpdate -> handleScreenCaptureUpdate(action)
is MainAction.Internal.ThemeUpdate -> handleAppThemeUpdated(action)
is MainAction.Internal.VaultUnlockStateChange -> handleVaultUnlockStateChange()
is MainAction.Internal.DynamicColorsUpdate -> handleDynamicColorsUpdate(action)
is MainAction.Internal.OnMobileErrorReportingReceive -> {
handleOnMobileErrorReportingReceive(action)
}
@@ -277,10 +269,6 @@ class MainViewModel @Inject constructor(
recreateUiAndGarbageCollect()
}
private fun handleDynamicColorsUpdate(action: MainAction.Internal.DynamicColorsUpdate) {
mutableStateFlow.update { it.copy(isDynamicColorsEnabled = action.isDynamicColorsEnabled) }
}
private fun handleFirstIntentReceived(action: MainAction.ReceiveFirstIntent) {
handleIntent(
intent = action.intent,
@@ -321,10 +309,10 @@ class MainViewModel @Inject constructor(
val hasGeneratorShortcut = intent.isPasswordGeneratorShortcut
val hasVaultShortcut = intent.isMyVaultShortcut
val hasAccountSecurityShortcut = intent.isAccountSecurityShortcut
val fido2CreateCredentialRequestData = intent.getFido2CreateCredentialRequestOrNull()
val completeRegistrationData = intent.getCompleteRegistrationDataIntentOrNull()
val createCredentialRequest = intent.getCreateCredentialRequestOrNull()
val getCredentialsRequest = intent.getGetCredentialsRequestOrNull()
val fido2AssertCredentialRequest = intent.getFido2AssertionRequestOrNull()
val fido2CredentialAssertionRequest = intent.getFido2AssertionRequestOrNull()
val fido2GetCredentialsRequest = intent.getFido2GetCredentialsRequestOrNull()
when {
passwordlessRequestData != null -> {
authRepository.activeUserId?.let {
@@ -382,43 +370,41 @@ class MainViewModel @Inject constructor(
)
}
createCredentialRequest != null -> {
fido2CreateCredentialRequestData != 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.
bitwardenCredentialManager.isUserVerified =
createCredentialRequest.isUserPreVerified
fido2CreateCredentialRequestData.isUserVerified
?.let { isVerified -> fido2CredentialManager.isUserVerified = isVerified }
specialCircumstanceManager.specialCircumstance =
SpecialCircumstance.ProviderCreateCredential(
createCredentialRequest = createCredentialRequest,
SpecialCircumstance.Fido2Save(
fido2CreateCredentialRequest = fido2CreateCredentialRequestData,
)
// Switch accounts if the selected user is not the active user.
if (authRepository.activeUserId != null &&
authRepository.activeUserId != createCredentialRequest.userId
authRepository.activeUserId != fido2CreateCredentialRequestData.userId
) {
authRepository.switchAccount(createCredentialRequest.userId)
authRepository.switchAccount(fido2CreateCredentialRequestData.userId)
}
}
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.
bitwardenCredentialManager.isUserVerified =
fido2AssertCredentialRequest.isUserPreVerified
fido2CredentialAssertionRequest != null -> {
// If device biometric verification was performed as part of single-tap
// authentication, set the user's verification state to the device result.
// Otherwise, retain the verification state as-is.
fido2CredentialAssertionRequest.isUserVerified
?.let { isVerified -> fido2CredentialManager.isUserVerified = isVerified }
specialCircumstanceManager.specialCircumstance =
SpecialCircumstance.Fido2Assertion(
fido2AssertionRequest = fido2AssertCredentialRequest,
fido2AssertionRequest = fido2CredentialAssertionRequest,
)
}
getCredentialsRequest != null -> {
fido2GetCredentialsRequest != null -> {
specialCircumstanceManager.specialCircumstance =
SpecialCircumstance.ProviderGetCredentials(
getCredentialsRequest = getCredentialsRequest,
SpecialCircumstance.Fido2GetCredentials(
fido2GetCredentialsRequest = fido2GetCredentialsRequest,
)
}
@@ -494,7 +480,6 @@ class MainViewModel @Inject constructor(
data class MainState(
val theme: AppTheme,
val isScreenCaptureAllowed: Boolean,
val isDynamicColorsEnabled: Boolean,
private val isErrorReportingDialogEnabled: Boolean,
) : Parcelable {
/**
@@ -585,13 +570,6 @@ sealed class MainAction {
* Indicates a relevant change in the current vault lock state.
*/
data object VaultUnlockStateChange : Internal()
/**
* Indicates that the dynamic colors state has changed.
*/
data class DynamicColorsUpdate(
val isDynamicColorsEnabled: Boolean,
) : Internal()
}
}

View File

@@ -1,11 +1,11 @@
package com.x8bit.bitwarden.data.auth.datasource.disk
import com.bitwarden.network.model.SyncResponseJson
import com.bitwarden.network.provider.AppIdProvider
import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountTokensJson
import com.x8bit.bitwarden.data.auth.datasource.disk.model.NewDeviceNoticeState
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
@@ -13,7 +13,7 @@ import java.time.Instant
* Primary access point for disk information.
*/
@Suppress("TooManyFunctions")
interface AuthDiskSource : AppIdProvider {
interface AuthDiskSource {
/**
* The currently persisted authenticator sync symmetric key. This key is used for
@@ -21,6 +21,13 @@ interface AuthDiskSource : AppIdProvider {
*/
var authenticatorSyncSymmetricKey: ByteArray?
/**
* Retrieves a unique ID for the application that is stored locally. This will generate a new
* one if it does not yet exist and it will only be reset for new installs or when clearing
* application data.
*/
val uniqueAppId: String
/**
* The currently persisted saved email address (or `null` if not set).
*/
@@ -337,6 +344,16 @@ interface AuthDiskSource : AppIdProvider {
*/
fun getShowImportLoginsFlow(userId: String): Flow<Boolean?>
/**
* Gets the new device notice state for the given [userId].
*/
fun getNewDeviceNoticeState(userId: String): NewDeviceNoticeState
/**
* Stores the new device notice state for the given [userId].
*/
fun storeNewDeviceNoticeState(userId: String, newState: NewDeviceNoticeState?)
/**
* Gets the last lock timestamp for the given [userId].
*/

View File

@@ -1,15 +1,17 @@
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.NewDeviceNoticeDisplayStatus
import com.x8bit.bitwarden.data.auth.datasource.disk.model.NewDeviceNoticeState
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
@@ -47,6 +49,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 NEW_DEVICE_NOTICE_STATE = "newDeviceNoticeState"
private const val LAST_LOCK_TIMESTAMP = "lastLockTimestamp"
/**
@@ -486,6 +489,22 @@ class AuthDiskSourceImpl(
getMutableShowImportLoginsFlow(userId)
.onSubscription { emit(getShowImportLogins(userId)) }
override fun getNewDeviceNoticeState(userId: String): NewDeviceNoticeState {
return getString(key = NEW_DEVICE_NOTICE_STATE.appendIdentifier(userId))?.let {
json.decodeFromStringOrNull(it)
} ?: NewDeviceNoticeState(
displayStatus = NewDeviceNoticeDisplayStatus.HAS_NOT_SEEN,
lastSeenDate = null,
)
}
override fun storeNewDeviceNoticeState(userId: String, newState: NewDeviceNoticeState?) {
putString(
key = NEW_DEVICE_NOTICE_STATE.appendIdentifier(userId),
value = newState?.let { json.encodeToString(it) },
)
}
override fun getLastLockTimestamp(userId: String): Instant? {
return getLong(key = LAST_LOCK_TIMESTAMP.appendIdentifier(userId))?.let {
Instant.ofEpochMilli(it)

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,8 +1,7 @@
package com.x8bit.bitwarden.data.auth.datasource.disk.model
import com.bitwarden.data.datasource.disk.model.EnvironmentUrlDataJson
import com.bitwarden.network.model.KdfTypeJson
import com.bitwarden.network.model.UserDecryptionOptionsJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.KdfTypeJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.UserDecryptionOptionsJson
import kotlinx.serialization.Contextual
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName

View File

@@ -1,4 +1,4 @@
package com.bitwarden.data.datasource.disk.model
package com.x8bit.bitwarden.data.auth.datasource.disk.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -0,0 +1,60 @@
package com.x8bit.bitwarden.data.auth.datasource.disk.model
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import java.time.ZonedDateTime
/**
* Describes the current display status of the new device notice screen.
*/
@Serializable
enum class NewDeviceNoticeDisplayStatus {
/**
* The user has seen the screen and indicated they can access their email.
*/
@SerialName("canAccessEmail")
CAN_ACCESS_EMAIL,
/**
* The user has indicated they can access their email
* as specified by the Permanent mode of the notice.
*/
@SerialName("canAccessEmailPermanent")
CAN_ACCESS_EMAIL_PERMANENT,
/**
* The user has not seen the screen.
*/
@SerialName("hasNotSeen")
HAS_NOT_SEEN,
/**
* The user has seen the screen and selected "remind me later".
*/
@SerialName("hasSeen")
HAS_SEEN,
}
/**
* The state of the new device notice screen.
*/
@Suppress("MagicNumber")
@Serializable
data class NewDeviceNoticeState(
@SerialName("displayStatus")
val displayStatus: NewDeviceNoticeDisplayStatus,
@SerialName("lastSeenDate")
@Contextual
val lastSeenDate: ZonedDateTime?,
) {
/**
* Whether the [lastSeenDate] is at least 7 days old.
*/
val shouldDisplayNoticeIfSeen = lastSeenDate
?.isBefore(
ZonedDateTime.now().minusDays(7),
)
?: false
}

View File

@@ -1,11 +1,11 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import com.bitwarden.network.model.CreateAccountKeysRequest
import com.bitwarden.network.model.DeleteAccountRequestJson
import com.bitwarden.network.model.NetworkResult
import com.bitwarden.network.model.ResetPasswordRequestJson
import com.bitwarden.network.model.SetPasswordRequestJson
import com.bitwarden.network.model.VerifyOtpRequestJson
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.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.NetworkResult
import retrofit2.http.Body
import retrofit2.http.HTTP
import retrofit2.http.POST
@@ -13,7 +13,7 @@ import retrofit2.http.POST
/**
* Defines raw calls under the /accounts API with authentication applied.
*/
internal interface AuthenticatedAccountsApi {
interface AuthenticatedAccountsApi {
/**
* Converts the currently active account to a key-connector account.

View File

@@ -1,9 +1,9 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import com.bitwarden.network.model.AuthRequestRequestJson
import com.bitwarden.network.model.AuthRequestUpdateRequestJson
import com.bitwarden.network.model.AuthRequestsResponseJson
import com.bitwarden.network.model.NetworkResult
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestUpdateRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestsResponseJson
import com.x8bit.bitwarden.data.platform.datasource.network.model.NetworkResult
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
@@ -14,7 +14,7 @@ import retrofit2.http.Path
/**
* Defines authenticated raw calls under the /auth-requests API.
*/
internal interface AuthenticatedAuthRequestsApi {
interface AuthenticatedAuthRequestsApi {
/**
* Notifies the server of a new admin authentication request.

View File

@@ -1,9 +1,9 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import androidx.annotation.Keep
import com.bitwarden.network.model.NetworkResult
import com.bitwarden.network.model.TrustedDeviceKeysRequestJson
import com.bitwarden.network.model.TrustedDeviceKeysResponseJson
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.model.NetworkResult
import retrofit2.http.Body
import retrofit2.http.PUT
import retrofit2.http.Path
@@ -12,7 +12,7 @@ import retrofit2.http.Path
* Defines raw calls under the /devices API that require authentication.
*/
@Keep
internal interface AuthenticatedDevicesApi {
interface AuthenticatedDevicesApi {
@PUT("/devices/{appId}/keys")
suspend fun updateTrustedDeviceKeys(
@Path(value = "appId") appId: String,

View File

@@ -1,8 +1,8 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import androidx.annotation.Keep
import com.bitwarden.network.model.KeyConnectorMasterKeyRequestJson
import com.bitwarden.network.model.NetworkResult
import com.x8bit.bitwarden.data.auth.datasource.network.model.KeyConnectorMasterKeyRequestJson
import com.x8bit.bitwarden.data.platform.datasource.network.model.NetworkResult
import retrofit2.http.Body
import retrofit2.http.POST
import retrofit2.http.Url
@@ -11,7 +11,7 @@ import retrofit2.http.Url
* Defines raw calls specific for key connectors that use custom urls.
*/
@Keep
internal interface AuthenticatedKeyConnectorApi {
interface AuthenticatedKeyConnectorApi {
@POST
suspend fun storeMasterKeyToKeyConnector(
@Url url: String,

View File

@@ -1,19 +1,18 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import com.bitwarden.network.model.NetworkResult
import com.bitwarden.network.model.OrganizationAutoEnrollStatusResponseJson
import com.bitwarden.network.model.OrganizationKeysResponseJson
import com.bitwarden.network.model.OrganizationResetPasswordEnrollRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.OrganizationAutoEnrollStatusResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.OrganizationKeysResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.OrganizationResetPasswordEnrollRequestJson
import com.x8bit.bitwarden.data.platform.datasource.network.model.NetworkResult
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
import retrofit2.http.PUT
import retrofit2.http.Path
/**
* Defines raw calls under the authenticated /organizations API.
*/
internal interface AuthenticatedOrganizationApi {
interface AuthenticatedOrganizationApi {
/**
* Enrolls this user in the organization's password reset.
*/
@@ -39,12 +38,4 @@ internal interface AuthenticatedOrganizationApi {
suspend fun getOrganizationKeys(
@Path("id") organizationId: String,
): NetworkResult<OrganizationKeysResponseJson>
/**
* Leaves the organization
*/
@POST("/organizations/{id}/leave")
suspend fun leaveOrganization(
@Path("id") organizationId: String,
): NetworkResult<Unit>
}

View File

@@ -1,6 +1,6 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import com.bitwarden.network.model.NetworkResult
import com.x8bit.bitwarden.data.platform.datasource.network.model.NetworkResult
import okhttp3.ResponseBody
import retrofit2.http.GET
import retrofit2.http.Path
@@ -9,7 +9,7 @@ import retrofit2.http.Path
* Defines endpoints for the "have I been pwned" API. For docs see
* https://haveibeenpwned.com/API/v2.
*/
internal interface HaveIBeenPwnedApi {
interface HaveIBeenPwnedApi {
@GET("/range/{hashPrefix}")
suspend fun fetchBreachedPasswords(

View File

@@ -1,11 +1,11 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import com.bitwarden.network.model.KeyConnectorKeyRequestJson
import com.bitwarden.network.model.NetworkResult
import com.bitwarden.network.model.PasswordHintRequestJson
import com.bitwarden.network.model.ResendEmailRequestJson
import com.bitwarden.network.model.ResendNewDeviceOtpRequestJson
import com.bitwarden.network.util.HEADER_KEY_AUTHORIZATION
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.auth.datasource.network.model.ResendNewDeviceOtpRequestJson
import com.x8bit.bitwarden.data.platform.datasource.network.model.NetworkResult
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_KEY_AUTHORIZATION
import retrofit2.http.Body
import retrofit2.http.Header
import retrofit2.http.POST
@@ -13,7 +13,7 @@ import retrofit2.http.POST
/**
* Defines raw calls under the /accounts API.
*/
internal interface UnauthenticatedAccountsApi {
interface UnauthenticatedAccountsApi {
@POST("/accounts/password-hint")
suspend fun passwordHintRequest(
@Body body: PasswordHintRequestJson,

View File

@@ -1,8 +1,8 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import com.bitwarden.network.model.AuthRequestRequestJson
import com.bitwarden.network.model.AuthRequestsResponseJson
import com.bitwarden.network.model.NetworkResult
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.AuthRequestsResponseJson
import com.x8bit.bitwarden.data.platform.datasource.network.model.NetworkResult
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
@@ -13,7 +13,7 @@ import retrofit2.http.Query
/**
* Defines unauthenticated raw calls under the /auth-requests API.
*/
internal interface UnauthenticatedAuthRequestsApi {
interface UnauthenticatedAuthRequestsApi {
/**
* Notifies the server of a new authentication request.

View File

@@ -1,13 +1,13 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import com.bitwarden.network.model.NetworkResult
import com.x8bit.bitwarden.data.platform.datasource.network.model.NetworkResult
import retrofit2.http.GET
import retrofit2.http.Header
/**
* Defines raw calls under the /devices API that do not require authentication.
*/
internal interface UnauthenticatedDevicesApi {
interface UnauthenticatedDevicesApi {
@GET("/devices/knowndevice")
suspend fun getIsKnownDevice(
@Header(value = "X-Request-Email") emailAddress: String,

View File

@@ -1,16 +1,16 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import com.bitwarden.network.model.GetTokenResponseJson
import com.bitwarden.network.model.NetworkResult
import com.bitwarden.network.model.PreLoginRequestJson
import com.bitwarden.network.model.PreLoginResponseJson
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.SendVerificationEmailRequestJson
import com.bitwarden.network.model.VerifyEmailTokenRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.GetTokenResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.PreLoginRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.PreLoginResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.PrevalidateSsoResponseJson
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.SendVerificationEmailRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifyEmailTokenRequestJson
import com.x8bit.bitwarden.data.platform.datasource.network.model.NetworkResult
import kotlinx.serialization.json.JsonPrimitive
import retrofit2.Call
import retrofit2.http.Body
@@ -24,7 +24,7 @@ import retrofit2.http.Query
/**
* Defines raw calls under the /identity API.
*/
internal interface UnauthenticatedIdentityApi {
interface UnauthenticatedIdentityApi {
@POST("/connect/token")
@Suppress("LongParameterList")

View File

@@ -1,10 +1,10 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import androidx.annotation.Keep
import com.bitwarden.network.model.KeyConnectorMasterKeyRequestJson
import com.bitwarden.network.model.KeyConnectorMasterKeyResponseJson
import com.bitwarden.network.model.NetworkResult
import com.bitwarden.network.util.HEADER_KEY_AUTHORIZATION
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.platform.datasource.network.model.NetworkResult
import com.x8bit.bitwarden.data.platform.datasource.network.util.HEADER_KEY_AUTHORIZATION
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.Header
@@ -15,7 +15,7 @@ import retrofit2.http.Url
* Defines raw calls specific for key connectors that use custom urls.
*/
@Keep
internal interface UnauthenticatedKeyConnectorApi {
interface UnauthenticatedKeyConnectorApi {
@POST
suspend fun storeMasterKeyToKeyConnector(
@Url url: String,

View File

@@ -1,17 +1,17 @@
package com.bitwarden.network.api
package com.x8bit.bitwarden.data.auth.datasource.network.api
import com.bitwarden.network.model.NetworkResult
import com.bitwarden.network.model.OrganizationDomainSsoDetailsRequestJson
import com.bitwarden.network.model.OrganizationDomainSsoDetailsResponseJson
import com.bitwarden.network.model.VerifiedOrganizationDomainSsoDetailsRequest
import com.bitwarden.network.model.VerifiedOrganizationDomainSsoDetailsResponse
import com.x8bit.bitwarden.data.auth.datasource.network.model.OrganizationDomainSsoDetailsRequestJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.OrganizationDomainSsoDetailsResponseJson
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifiedOrganizationDomainSsoDetailsRequest
import com.x8bit.bitwarden.data.auth.datasource.network.model.VerifiedOrganizationDomainSsoDetailsResponse
import com.x8bit.bitwarden.data.platform.datasource.network.model.NetworkResult
import retrofit2.http.Body
import retrofit2.http.POST
/**
* Defines raw calls under the /organizations API.
*/
internal interface UnauthenticatedOrganizationApi {
interface UnauthenticatedOrganizationApi {
/**
* Checks for the claimed domain organization of an email for SSO purposes.
*/
@@ -21,7 +21,7 @@ internal interface UnauthenticatedOrganizationApi {
): NetworkResult<OrganizationDomainSsoDetailsResponseJson>
/**
* Checks for the verified organization domains of an email for SSO purposes.
* Checks for the verfied organization domains of an email for SSO purposes.
*/
@POST("/organizations/domain/sso/verified")
suspend fun getVerifiedOrganizationDomainsByEmail(

View File

@@ -1,17 +1,26 @@
package com.x8bit.bitwarden.data.auth.datasource.network.di
import com.bitwarden.network.BitwardenServiceClient
import com.bitwarden.network.service.AccountsService
import com.bitwarden.network.service.AuthRequestsService
import com.bitwarden.network.service.DevicesService
import com.bitwarden.network.service.HaveIBeenPwnedService
import com.bitwarden.network.service.IdentityService
import com.bitwarden.network.service.NewAuthRequestService
import com.bitwarden.network.service.OrganizationService
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.x8bit.bitwarden.data.platform.datasource.network.retrofit.Retrofits
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import retrofit2.create
import javax.inject.Singleton
/**
@@ -24,42 +33,70 @@ object AuthNetworkModule {
@Provides
@Singleton
fun providesAccountService(
bitwardenServiceClient: BitwardenServiceClient,
): AccountsService = bitwardenServiceClient.accountsService
retrofits: Retrofits,
json: Json,
): AccountsService = AccountsServiceImpl(
unauthenticatedAccountsApi = retrofits.unauthenticatedApiRetrofit.create(),
authenticatedAccountsApi = retrofits.authenticatedApiRetrofit.create(),
unauthenticatedKeyConnectorApi = retrofits.createStaticRetrofit().create(),
authenticatedKeyConnectorApi = retrofits
.createStaticRetrofit(isAuthenticated = true)
.create(),
json = json,
)
@Provides
@Singleton
fun providesAuthRequestsService(
bitwardenServiceClient: BitwardenServiceClient,
): AuthRequestsService = bitwardenServiceClient.authRequestsService
retrofits: Retrofits,
): AuthRequestsService = AuthRequestsServiceImpl(
authenticatedAuthRequestsApi = retrofits.authenticatedApiRetrofit.create(),
)
@Provides
@Singleton
fun providesDevicesService(
bitwardenServiceClient: BitwardenServiceClient,
): DevicesService = bitwardenServiceClient.devicesService
retrofits: Retrofits,
): DevicesService = DevicesServiceImpl(
authenticatedDevicesApi = retrofits.authenticatedApiRetrofit.create(),
unauthenticatedDevicesApi = retrofits.unauthenticatedApiRetrofit.create(),
)
@Provides
@Singleton
fun providesIdentityService(
bitwardenServiceClient: BitwardenServiceClient,
): IdentityService = bitwardenServiceClient.identityService
retrofits: Retrofits,
json: Json,
): IdentityService = IdentityServiceImpl(
unauthenticatedIdentityApi = retrofits.unauthenticatedIdentityRetrofit.create(),
json = json,
)
@Provides
@Singleton
fun providesHaveIBeenPwnedService(
bitwardenServiceClient: BitwardenServiceClient,
): HaveIBeenPwnedService = bitwardenServiceClient.haveIBeenPwnedService
retrofits: Retrofits,
): HaveIBeenPwnedService = HaveIBeenPwnedServiceImpl(
api = retrofits
.createStaticRetrofit(baseUrl = "https://api.pwnedpasswords.com")
.create(),
)
@Provides
@Singleton
fun providesNewAuthRequestService(
bitwardenServiceClient: BitwardenServiceClient,
): NewAuthRequestService = bitwardenServiceClient.newAuthRequestService
retrofits: Retrofits,
): NewAuthRequestService = NewAuthRequestServiceImpl(
authenticatedAuthRequestsApi = retrofits.authenticatedApiRetrofit.create(),
unauthenticatedAuthRequestsApi = retrofits.unauthenticatedApiRetrofit.create(),
)
@Provides
@Singleton
fun providesOrganizationService(
bitwardenServiceClient: BitwardenServiceClient,
): OrganizationService = bitwardenServiceClient.organizationService
retrofits: Retrofits,
): OrganizationService = OrganizationServiceImpl(
authenticatedOrganizationApi = retrofits.authenticatedApiRetrofit.create(),
unauthenticatedOrganizationApi = retrofits.unauthenticatedApiRetrofit.create(),
)
}

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,7 +1,7 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import androidx.annotation.Keep
import com.bitwarden.core.data.serializer.BaseEnumeratedIntSerializer
import com.x8bit.bitwarden.data.platform.datasource.network.serializer.BaseEnumeratedIntSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
@@ -119,11 +119,6 @@ sealed class GetTokenResponseJson {
*/
data object NewDeviceVerification : InvalidType()
/**
* Represents an invalid response indicating that a new device verification is required.
*/
data object EncryptionKeyMigrationRequired : InvalidType()
/**
* Represents generic invalid response
*/
@@ -133,12 +128,6 @@ sealed class GetTokenResponseJson {
val invalidType: InvalidType
get() = if (errorMessage?.lowercase() == "new device verification required") {
InvalidType.NewDeviceVerification
} else if (errorMessage
?.lowercase()
?.contains(
"encryption key migration is required. please log in to the web vault at",
) == true) {
InvalidType.EncryptionKeyMigrationRequired
} else {
InvalidType.GenericInvalid
}

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
/**
* Hold the authentication information for different login methods.

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,7 +1,7 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import androidx.annotation.Keep
import com.bitwarden.core.data.serializer.BaseEnumeratedIntSerializer
import com.x8bit.bitwarden.data.platform.datasource.network.serializer.BaseEnumeratedIntSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,6 +1,6 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import com.bitwarden.network.serializer.BaseSurrogateSerializer
import com.x8bit.bitwarden.data.platform.datasource.network.serializer.BaseSurrogateSerializer
import kotlinx.serialization.Serializable
/**

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,5 +1,6 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterFinishRequestJson.Keys
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,5 +1,6 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson.Keys
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
@@ -19,7 +19,7 @@ sealed class RegisterResponseJson {
@Serializable
data class Success(
@SerialName("captchaBypassToken")
val captchaBypassToken: String?,
val captchaBypassToken: String,
) : RegisterResponseJson()
/**

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,9 +1,7 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonNames
/**
* The response body for sending a verification email.
@@ -28,24 +26,20 @@ sealed class SendVerificationEmailResponseJson {
* The values in the array should be used for display to the user, since the keys tend to come
* back as nonsense. (eg: empty string key)
*/
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class Invalid(
@JsonNames("message")
@SerialName("message")
private val invalidMessage: String? = null,
@SerialName("Message")
private val errorMessage: String? = null,
@SerialName("validationErrors")
private val validationErrors: Map<String, List<String>>?,
val validationErrors: Map<String, List<String>>?,
) : SendVerificationEmailResponseJson() {
/**
* A generic error message.
*/
val message: String?
get() = validationErrors
?.values
?.firstOrNull()
?.firstOrNull()
?: errorMessage
val message: String? get() = invalidMessage ?: errorMessage
}
}

View File

@@ -1,6 +1,6 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import com.bitwarden.network.model.RegisterRequestJson.Keys
import com.x8bit.bitwarden.data.auth.datasource.network.model.RegisterRequestJson.Keys
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.Contextual
import kotlinx.serialization.SerialName

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName

View File

@@ -1,7 +1,7 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
import androidx.annotation.Keep
import com.bitwarden.core.data.serializer.BaseEnumeratedIntSerializer
import com.x8bit.bitwarden.data.platform.datasource.network.serializer.BaseEnumeratedIntSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@@ -1,4 +1,4 @@
package com.bitwarden.network.model
package com.x8bit.bitwarden.data.auth.datasource.network.model
/**
* Hold the information necessary to add two-factor authorization

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