Compare commits
3 Commits
2f7ea5e255
...
ce239499f8
Author | SHA1 | Date |
---|---|---|
Federico Maccaroni | ce239499f8 | |
Vince Grassia | a333e72448 | |
Federico Maccaroni | 2cdb5a9c74 |
|
@ -65,7 +65,6 @@ jobs:
|
|||
description: 'Deployment ${{ steps.version.outputs.version }} from branch ${{ steps.branch.outputs.branch-name }}'
|
||||
task: release
|
||||
|
||||
|
||||
- name: Download all artifacts
|
||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||
uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4
|
||||
|
@ -152,9 +151,7 @@ jobs:
|
|||
node-version: '16.x'
|
||||
|
||||
- name: Set up F-Droid server
|
||||
run: |
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get -qqy install --no-install-recommends fdroidserver wget
|
||||
run: pip install git+https://gitlab.com/fdroid/fdroidserver.git
|
||||
|
||||
- name: Set up Git credentials
|
||||
env:
|
||||
|
@ -167,9 +164,10 @@ jobs:
|
|||
|
||||
- name: Print environment
|
||||
run: |
|
||||
node --version
|
||||
npm --version
|
||||
git --version
|
||||
echo "Node Version: $(node --version)"
|
||||
echo "NPM Version: $(npm --version)"
|
||||
echo "Git Version: $(git --version)"
|
||||
echo "F-Droid Server Version: $(fdroid --version)"
|
||||
echo "GitHub ref: $GITHUB_REF"
|
||||
echo "GitHub event: $GITHUB_EVENT"
|
||||
|
||||
|
@ -194,27 +192,30 @@ jobs:
|
|||
env:
|
||||
FDROID_STORE_KEYSTORE_PASSWORD: ${{ secrets.FDROID_STORE_KEYSTORE_PASSWORD }}
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE
|
||||
# Create required directories.
|
||||
mkdir dist
|
||||
cp CNAME ./dist
|
||||
cd store
|
||||
chmod 600 fdroid/config.py fdroid/keystore.jks
|
||||
mkdir -p temp/fdroid
|
||||
mkdir -p store/temp/fdroid
|
||||
mkdir -p store/fdroid/repo
|
||||
|
||||
# Configure F-Droid server.
|
||||
cp CNAME dist/
|
||||
chmod 600 store/fdroid/config.yml store/fdroid/keystore.jks
|
||||
TEMP_DIR="$GITHUB_WORKSPACE/store/temp/fdroid"
|
||||
cd fdroid
|
||||
echo "keypass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
||||
echo "keystorepass=\"$FDROID_STORE_KEYSTORE_PASSWORD\"" >>config.py
|
||||
echo "local_copy_dir=\"$TEMP_DIR\"" >>config.py
|
||||
mkdir -p repo
|
||||
mv $GITHUB_WORKSPACE/com.x8bit.bitwarden-fdroid.apk ./repo/
|
||||
echo "keypass: $FDROID_STORE_KEYSTORE_PASSWORD" >> store/fdroid/config.yml
|
||||
echo "keystorepass: $FDROID_STORE_KEYSTORE_PASSWORD" >> store/fdroid/config.yml
|
||||
echo "local_copy_dir: $TEMP_DIR" >> store/fdroid/config.yml
|
||||
mv $GITHUB_WORKSPACE/com.x8bit.bitwarden-fdroid.apk store/fdroid/repo/
|
||||
|
||||
# Run update and deploy.
|
||||
cd store/fdroid
|
||||
fdroid update
|
||||
fdroid server update
|
||||
cd ..
|
||||
rm -rf temp/fdroid/archive
|
||||
mv -v temp/fdroid ../dist
|
||||
cd fdroid
|
||||
cp index.html btn.png qr.png ../../dist/fdroid
|
||||
cd $GITHUB_WORKSPACE
|
||||
fdroid deploy
|
||||
cd ../..
|
||||
|
||||
# Move files for distribution.
|
||||
rm -rf store/temp/fdroid/archive
|
||||
mv -v store/temp/fdroid dist
|
||||
cp store/fdroid/index.html store/fdroid/btn.png store/fdroid/qr.png dist/fdroid
|
||||
|
||||
- name: Deploy to gh-pages
|
||||
if: ${{ inputs.release_type != 'Dry Run' }}
|
||||
|
|
|
@ -1,26 +1,31 @@
|
|||
using Android.App;
|
||||
using System.Diagnostics;
|
||||
using Android.App;
|
||||
using Android.Content;
|
||||
using Android.Content.PM;
|
||||
using Android.OS;
|
||||
using Android.Runtime;
|
||||
using AndroidX.Activity.Result;
|
||||
using AndroidX.Activity.Result.Contract;
|
||||
using AndroidX.Credentials;
|
||||
using AndroidX.Credentials.Exceptions;
|
||||
using AndroidX.Credentials.Provider;
|
||||
using AndroidX.Credentials.WebAuthn;
|
||||
using Bit.App.Droid.Utilities;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.Core.Utilities.Fido2;
|
||||
using Bit.Core.Services;
|
||||
using Bit.App.Droid.Utilities;
|
||||
using Bit.App.Platforms.Android.Autofill;
|
||||
using AndroidX.Credentials.Exceptions;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Resources.Localization;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities;
|
||||
using Bit.Core.Utilities.Fido2;
|
||||
using Org.Json;
|
||||
|
||||
namespace Bit.Droid.Autofill
|
||||
{
|
||||
[Activity(
|
||||
NoHistory = true,
|
||||
LaunchMode = LaunchMode.SingleTop)]
|
||||
NoHistory = false,
|
||||
LaunchMode = LaunchMode.SingleInstance)]
|
||||
[Register("com.x8bit.bitwarden.CredentialProviderSelectionActivity")]
|
||||
public class CredentialProviderSelectionActivity : MauiAppCompatActivity
|
||||
{
|
||||
private LazyResolve<IFido2MediatorService> _fido2MediatorService = new LazyResolve<IFido2MediatorService>();
|
||||
|
@ -31,6 +36,8 @@ namespace Bit.Droid.Autofill
|
|||
private LazyResolve<IUserVerificationMediatorService> _userVerificationMediatorService = new LazyResolve<IUserVerificationMediatorService>();
|
||||
private LazyResolve<IDeviceActionService> _deviceActionService = new LazyResolve<IDeviceActionService>();
|
||||
|
||||
private ActivityResultLauncher _activityResultLauncher;
|
||||
|
||||
protected override void OnCreate(Bundle bundle)
|
||||
{
|
||||
Intent?.Validate();
|
||||
|
@ -100,8 +107,14 @@ namespace Bit.Droid.Autofill
|
|||
cipherId,
|
||||
false,
|
||||
() => hasVaultBeenUnlockedInThisTransaction,
|
||||
RpId
|
||||
);
|
||||
RpId
|
||||
);
|
||||
|
||||
_activityResultLauncher = RegisterForActivityResult(new ActivityResultContracts.StartActivityForResult(),
|
||||
new ActivityResultCallback(result =>
|
||||
{
|
||||
_fido2GetAssertionUserInterface.Value.ConfirmVaultUnlocked(result.ResultCode == (int)Result.Ok);
|
||||
}));
|
||||
|
||||
var clientAssertParams = new Fido2ClientAssertCredentialParams
|
||||
{
|
||||
|
@ -171,6 +184,19 @@ namespace Bit.Droid.Autofill
|
|||
}
|
||||
}
|
||||
|
||||
public void LaunchToUnlock()
|
||||
{
|
||||
if (_activityResultLauncher is null)
|
||||
{
|
||||
throw new InvalidOperationException("There is no activity result launcher available");
|
||||
}
|
||||
|
||||
var intent = new Intent(this, typeof(MainActivity));
|
||||
intent.PutExtra(CredentialProviderConstants.Fido2CredentialAction, CredentialProviderConstants.Fido2CredentialNeedsUnlockingAgainBecauseImmediateTimeout);
|
||||
|
||||
_activityResultLauncher.Launch(intent);
|
||||
}
|
||||
|
||||
private void FailAndFinish()
|
||||
{
|
||||
var result = new Intent();
|
||||
|
@ -180,4 +206,12 @@ namespace Bit.Droid.Autofill
|
|||
Finish();
|
||||
}
|
||||
}
|
||||
|
||||
public class ActivityResultCallback : Java.Lang.Object, IActivityResultCallback
|
||||
{
|
||||
readonly Action<ActivityResult> _callback;
|
||||
public ActivityResultCallback(Action<ActivityResult> callback) => _callback = callback;
|
||||
public ActivityResultCallback(TaskCompletionSource<ActivityResult> tcs) => _callback = tcs.SetResult;
|
||||
public void OnActivityResult(Java.Lang.Object p0) => _callback((ActivityResult)p0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities.Fido2;
|
||||
using Bit.Droid.Autofill;
|
||||
|
||||
namespace Bit.App.Platforms.Android.Autofill
|
||||
{
|
||||
|
@ -10,6 +11,11 @@ namespace Bit.App.Platforms.Android.Autofill
|
|||
bool userVerified,
|
||||
Func<bool> hasVaultBeenUnlockedInThisTransaction,
|
||||
string rpId);
|
||||
|
||||
/// <summary>
|
||||
/// Call this after the vault was unlocked so that Fido2 credential autofill can proceed.
|
||||
/// </summary>
|
||||
void ConfirmVaultUnlocked(bool unlocked);
|
||||
}
|
||||
|
||||
public class Fido2GetAssertionUserInterface : Core.Utilities.Fido2.Fido2GetAssertionUserInterface, IFido2AndroidGetAssertionUserInterface
|
||||
|
@ -19,6 +25,8 @@ namespace Bit.App.Platforms.Android.Autofill
|
|||
private readonly ICipherService _cipherService;
|
||||
private readonly IUserVerificationMediatorService _userVerificationMediatorService;
|
||||
|
||||
private TaskCompletionSource<bool> _unlockVaultTcs;
|
||||
|
||||
public Fido2GetAssertionUserInterface(IStateService stateService,
|
||||
IVaultTimeoutService vaultTimeoutService,
|
||||
ICipherService cipherService,
|
||||
|
@ -46,11 +54,38 @@ namespace Bit.App.Platforms.Android.Autofill
|
|||
{
|
||||
if (!await _stateService.IsAuthenticatedAsync() || await _vaultTimeoutService.IsLockedAsync())
|
||||
{
|
||||
// this should never happen but just in case.
|
||||
throw new InvalidOperationException("Not authed or vault locked");
|
||||
if (await _stateService.GetVaultTimeoutAsync() != 0)
|
||||
{
|
||||
// this should never happen but just in case.
|
||||
throw new InvalidOperationException("Not authed or vault locked");
|
||||
}
|
||||
|
||||
// if vault timeout is immediate, then we need to unlock the vault
|
||||
if (!await NavigateAndWaitForUnlockAsync())
|
||||
{
|
||||
throw new InvalidOperationException("Couldn't unlock with immediate timeout");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ConfirmVaultUnlocked(bool unlocked) => _unlockVaultTcs?.TrySetResult(unlocked);
|
||||
|
||||
private async Task<bool> NavigateAndWaitForUnlockAsync()
|
||||
{
|
||||
var credentialProviderSelectionActivity = Platform.CurrentActivity as CredentialProviderSelectionActivity;
|
||||
if (credentialProviderSelectionActivity == null)
|
||||
{
|
||||
throw new InvalidOperationException("Can't get current activity");
|
||||
}
|
||||
|
||||
_unlockVaultTcs?.TrySetCanceled();
|
||||
_unlockVaultTcs = new TaskCompletionSource<bool>();
|
||||
|
||||
credentialProviderSelectionActivity.LaunchToUnlock();
|
||||
|
||||
return await _unlockVaultTcs.Task;
|
||||
}
|
||||
|
||||
private async Task<bool> VerifyUserAsync(string selectedCipherId, Fido2UserVerificationPreference userVerificationPreference, string rpId, bool vaultUnlockedDuringThisTransaction)
|
||||
{
|
||||
try
|
||||
|
|
|
@ -23,11 +23,14 @@ using Resource = Bit.Core.Resource;
|
|||
using Application = Android.App.Application;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities.Fido2;
|
||||
using Bit.Core.Utilities;
|
||||
|
||||
namespace Bit.Droid.Services
|
||||
{
|
||||
public class DeviceActionService : IDeviceActionService
|
||||
{
|
||||
public const int DELAY_LOCK_LOGOUT_FOR_FIDO2_AUTOFILL_ON_IMMEDIATE_TIMEOUT_MS = 15000;
|
||||
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IMessagingService _messagingService;
|
||||
private AlertDialog _progressDialog;
|
||||
|
@ -578,6 +581,15 @@ namespace Bit.Droid.Services
|
|||
{
|
||||
await ExecuteFido2GetCredentialAsync(appOptions);
|
||||
}
|
||||
else if (appOptions.Fido2CredentialAction == CredentialProviderConstants.Fido2CredentialNeedsUnlockingAgainBecauseImmediateTimeout
|
||||
&&
|
||||
ServiceContainer.TryResolve<IVaultTimeoutService>(out var vaultTimeoutService))
|
||||
{
|
||||
vaultTimeoutService.DelayLockAndLogoutMs = DELAY_LOCK_LOGOUT_FOR_FIDO2_AUTOFILL_ON_IMMEDIATE_TIMEOUT_MS;
|
||||
|
||||
activity.SetResult(Result.Ok);
|
||||
activity.Finish();
|
||||
}
|
||||
else if (appOptions.Fido2CredentialAction == CredentialProviderConstants.Fido2CredentialCreate)
|
||||
{
|
||||
await ExecuteFido2CreateCredentialAsync();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
public const string Fido2CredentialCreate = "fido2CredentialCreate";
|
||||
public const string Fido2CredentialGet = "fido2CredentialGet";
|
||||
public const string Fido2CredentialAction = "fido2CredentialAction";
|
||||
public const string Fido2CredentialNeedsUnlockingAgainBecauseImmediateTimeout = "fido2CredentialNeedsUnlockingAgainBecauseImmediateTimeout";
|
||||
public const string CredentialProviderCipherId = "credentialProviderCipherId";
|
||||
public const string CredentialDataIntentExtra = "CREDENTIAL_DATA";
|
||||
public const string CredentialIdIntentExtra = "credId";
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
repo_url = "https://mobileapp.bitwarden.com/fdroid/repo"
|
||||
repo_name = "Bitwarden F-Droid Repo"
|
||||
repo_icon = "fdroid-icon.png"
|
||||
repo_description = """
|
||||
F-Droid repo for Bitwarden.
|
||||
"""
|
||||
|
||||
archive_older = 2
|
||||
archive_url = "https://does.not.exist"
|
||||
archive_name = "Bitwarden Archive Repo"
|
||||
archive_icon = "fdroid-icon.png"
|
||||
archive_description = """
|
||||
F-Droid archive repo for Bitwarden.
|
||||
"""
|
||||
|
||||
repo_keyalias = "bitwarden-Virtual-Machine"
|
||||
keystore = "keystore.jks"
|
||||
keydname = "CN=bitwarden-Virtual-Machine, OU=F-Droid"
|
|
@ -0,0 +1,18 @@
|
|||
---
|
||||
|
||||
repo_url: https://mobileapp.bitwarden.com/fdroid/repo
|
||||
repo_name: Bitwarden F-Droid Repo
|
||||
repo_icon: fdroid-icon.png
|
||||
repo_description: >-
|
||||
F-Droid repo for Bitwarden.
|
||||
|
||||
archive_older: 2
|
||||
archive_url: https://does.not.exist/archive
|
||||
archive_name: Bitwarden Archive Repo
|
||||
archive_icon: fdroid-icon.png
|
||||
archive_description: >-
|
||||
F-Droid archive repo for Bitwarden.
|
||||
|
||||
repo_keyalias: bitwarden-Virtual-Machine
|
||||
keystore: keystore.jks
|
||||
keydname: CN=bitwarden-Virtual-Machine, OU=F-Droid
|
Loading…
Reference in New Issue