mirror of
https://github.com/aliasvault/aliasvault.git
synced 2026-02-01 18:02:31 -05:00
Merge branch 'main' into 1404-update-datamodel-to-use-fields-based-approach
* main: Update changelogs for new build number Create release-checklist.md New Crowdin updates (#1443) Bump qs in /apps/mobile-app in the npm_and_yarn group across 1 directory Adjust exportable flags for passkey authentication Android activities Bump uri in /docs in the bundler group across 1 directory Update project.pbxproj objectVersion Create print-latest-changelogs.sh Update release.yml Create git-versioning-strategy.md Add 0.25.3 changelogs Add additional loading message feedback during passkey verification (#1440) Add origin verification to Android Passkey Authentication flow (#1440) New Crowdin updates (#1438)
This commit is contained in:
44
.github/workflows/release.yml
vendored
44
.github/workflows/release.yml
vendored
@@ -27,7 +27,33 @@ on:
|
||||
type: boolean
|
||||
|
||||
jobs:
|
||||
# Guard job to prevent releases from main branch
|
||||
valid-release:
|
||||
if: github.event_name == 'release'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check tag target
|
||||
run: |
|
||||
BRANCHES=$(git branch -r --contains $GITHUB_SHA)
|
||||
|
||||
echo "Tag is contained in:"
|
||||
echo "$BRANCHES"
|
||||
|
||||
if ! echo "$BRANCHES" | grep -q "origin/release/"; then
|
||||
echo "❌ Releases must come from a release/* branch, please recreate the release from a release branch"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Tag is on a release branch"
|
||||
|
||||
upload-install-script:
|
||||
needs: [valid-release]
|
||||
if: always() && (github.event_name != 'release' || needs.valid-release.result == 'success')
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
@@ -43,7 +69,8 @@ jobs:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build-chrome-extension:
|
||||
if: github.event_name == 'release' || inputs.build_browser_extensions
|
||||
needs: [valid-release]
|
||||
if: always() && (github.event_name != 'release' || needs.valid-release.result == 'success') && (github.event_name == 'release' || inputs.build_browser_extensions)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -58,7 +85,8 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build-firefox-extension:
|
||||
if: github.event_name == 'release' || inputs.build_browser_extensions
|
||||
needs: [valid-release]
|
||||
if: always() && (github.event_name != 'release' || needs.valid-release.result == 'success') && (github.event_name == 'release' || inputs.build_browser_extensions)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -73,7 +101,8 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build-edge-extension:
|
||||
if: github.event_name == 'release' || inputs.build_browser_extensions
|
||||
needs: [valid-release]
|
||||
if: always() && (github.event_name != 'release' || needs.valid-release.result == 'success') && (github.event_name == 'release' || inputs.build_browser_extensions)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -88,7 +117,8 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build-android-release:
|
||||
if: github.event_name == 'release' || inputs.build_mobile_apps
|
||||
needs: [valid-release]
|
||||
if: always() && (github.event_name != 'release' || needs.valid-release.result == 'success') && (github.event_name == 'release' || inputs.build_mobile_apps)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -107,7 +137,8 @@ jobs:
|
||||
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||
|
||||
build-and-push-docker-multi-container:
|
||||
if: github.event_name == 'release' || inputs.build_multi_container
|
||||
needs: [valid-release]
|
||||
if: always() && (github.event_name != 'release' || needs.valid-release.result == 'success') && (github.event_name == 'release' || inputs.build_multi_container)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -372,7 +403,8 @@ jobs:
|
||||
annotations: ${{ steps.installcli-meta.outputs.annotations }}
|
||||
|
||||
build-and-push-docker-all-in-one:
|
||||
if: github.event_name == 'release' || inputs.build_all_in_one
|
||||
needs: [valid-release]
|
||||
if: always() && (github.event_name != 'release' || needs.valid-release.result == 'success') && (github.event_name == 'release' || inputs.build_all_in_one)
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
@@ -40,13 +40,13 @@
|
||||
<!-- Passkey Authentication Activity -->
|
||||
<activity
|
||||
android:name=".credentialprovider.PasskeyAuthenticationActivity"
|
||||
android:exported="true"
|
||||
android:exported="false"
|
||||
android:theme="@style/PasskeyRegistrationTheme" />
|
||||
|
||||
<!-- Passkey Registration Activity -->
|
||||
<activity
|
||||
android:name=".credentialprovider.PasskeyRegistrationActivity"
|
||||
android:exported="true"
|
||||
android:exported="false"
|
||||
android:theme="@style/PasskeyRegistrationTheme"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:fitsSystemWindows="true" />
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
||||
package net.aliasvault.app.credentialprovider
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
@@ -8,6 +9,10 @@ import android.widget.TextView
|
||||
import androidx.credentials.provider.PendingIntentHandler
|
||||
import androidx.credentials.provider.ProviderGetCredentialRequest
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.aliasvault.app.R
|
||||
import net.aliasvault.app.utils.Helpers
|
||||
import net.aliasvault.app.vaultstore.VaultStore
|
||||
@@ -109,6 +114,19 @@ class PasskeyAuthenticationActivity : FragmentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the loading message displayed to the user.
|
||||
*/
|
||||
private fun updateLoadingMessage(messageResId: Int) {
|
||||
runOnUiThread {
|
||||
try {
|
||||
findViewById<TextView>(R.id.loadingMessage)?.text = getString(messageResId)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Could not update loading message", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the passkey authentication request and generate assertion.
|
||||
* Called after authentication (biometric or PIN) succeeds and vault is unlocked.
|
||||
@@ -120,107 +138,149 @@ class PasskeyAuthenticationActivity : FragmentActivity() {
|
||||
finish()
|
||||
return
|
||||
}
|
||||
try {
|
||||
// Extract passkey ID from intent
|
||||
val passkeyIdString = intent.getStringExtra(
|
||||
AliasVaultCredentialProviderService.EXTRA_PASSKEY_ID,
|
||||
)
|
||||
if (passkeyIdString == null) {
|
||||
Log.e(TAG, "No passkey ID in intent")
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
val passkeyId = UUID.fromString(passkeyIdString.uppercase())
|
||||
|
||||
// Get database connection from vault (should be unlocked at this point)
|
||||
val db = vaultStore.database
|
||||
if (db == null) {
|
||||
Log.e(TAG, "Database not available - vault may not be unlocked")
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
val passkey = vaultStore.getPasskeyById(passkeyId, db)
|
||||
if (passkey == null) {
|
||||
Log.e(TAG, "Passkey not found: $passkeyId")
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
val requestJson = intent.getStringExtra(
|
||||
AliasVaultCredentialProviderService.EXTRA_REQUEST_JSON,
|
||||
) ?: ""
|
||||
val requestObj = JSONObject(requestJson)
|
||||
|
||||
// Extract clientDataHash from the calling app's request
|
||||
// Browsers (Chrome, Firefox, Edge, etc.) provide this, native apps typically don't
|
||||
val providedClientDataHash: ByteArray? = providerRequest.credentialOptions
|
||||
.filterIsInstance<androidx.credentials.GetPublicKeyCredentialOption>()
|
||||
.firstOrNull()?.clientDataHash
|
||||
|
||||
// Determine clientDataHash and clientDataJson based on what caller provided
|
||||
val clientDataHash: ByteArray
|
||||
val clientDataJson: String?
|
||||
if (providedClientDataHash != null) {
|
||||
// Browser provided clientDataHash - use it directly
|
||||
// The browser has its own clientDataJSON with the web origin
|
||||
clientDataHash = providedClientDataHash
|
||||
clientDataJson = null
|
||||
} else {
|
||||
// Native app scenario - build clientDataJSON ourselves and hash it
|
||||
val challenge = requestObj.optString("challenge", "")
|
||||
val origin = requestObj.optString("origin", "https://${passkey.rpId}")
|
||||
val json = buildClientDataJson(challenge, origin)
|
||||
clientDataHash = sha256(json.toByteArray(Charsets.UTF_8))
|
||||
clientDataJson = json
|
||||
}
|
||||
|
||||
// Use PasskeyAuthenticator.getAssertion for signing
|
||||
val credentialId = PasskeyHelper.guidToBytes(passkey.id.toString())
|
||||
val prfInputs = extractPrfInputs(requestObj)
|
||||
val assertion = PasskeyAuthenticator.getAssertion(
|
||||
credentialId = credentialId,
|
||||
clientDataHash = clientDataHash,
|
||||
rpId = passkey.rpId,
|
||||
privateKeyJWK = passkey.privateKey,
|
||||
userId = passkey.userHandle,
|
||||
uvPerformed = true,
|
||||
prfInputs = prfInputs,
|
||||
prfSecret = passkey.prfKey,
|
||||
)
|
||||
|
||||
// Build response JSON
|
||||
val response = buildPublicKeyCredentialResponse(
|
||||
assertion = assertion,
|
||||
clientDataJson = clientDataJson,
|
||||
)
|
||||
|
||||
val resultIntent = Intent()
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
PendingIntentHandler.setGetCredentialResponse(resultIntent, response)
|
||||
setResult(RESULT_OK, resultIntent)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error setting credential response", e)
|
||||
try {
|
||||
PendingIntentHandler.setGetCredentialException(
|
||||
resultIntent,
|
||||
androidx.credentials.exceptions.GetCredentialUnknownException("Failed to generate assertion: ${e.message}"),
|
||||
)
|
||||
setResult(RESULT_OK, resultIntent)
|
||||
} catch (e2: Exception) {
|
||||
Log.e(TAG, "Error setting exception", e2)
|
||||
// Show retrieving status to user
|
||||
updateLoadingMessage(R.string.passkey_retrieving)
|
||||
|
||||
// Extract passkey ID from intent
|
||||
val passkeyIdString = intent.getStringExtra(
|
||||
AliasVaultCredentialProviderService.EXTRA_PASSKEY_ID,
|
||||
)
|
||||
if (passkeyIdString == null) {
|
||||
Log.e(TAG, "No passkey ID in intent")
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
return@launch
|
||||
}
|
||||
|
||||
val passkeyId = UUID.fromString(passkeyIdString.uppercase())
|
||||
|
||||
// Get database connection from vault
|
||||
val db = vaultStore.database
|
||||
if (db == null) {
|
||||
Log.e(TAG, "Database not available - vault may not be unlocked")
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
return@launch
|
||||
}
|
||||
|
||||
val passkey = vaultStore.getPasskeyById(passkeyId, db)
|
||||
if (passkey == null) {
|
||||
Log.e(TAG, "Passkey not found: $passkeyId")
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
return@launch
|
||||
}
|
||||
|
||||
val requestJson = intent.getStringExtra(
|
||||
AliasVaultCredentialProviderService.EXTRA_REQUEST_JSON,
|
||||
) ?: ""
|
||||
val requestObj = JSONObject(requestJson)
|
||||
|
||||
// Extract clientDataHash from the calling app's request
|
||||
// Browsers (Chrome, Firefox, Edge, etc.) provide this, native apps typically don't
|
||||
val providedClientDataHash: ByteArray? = providerRequest.credentialOptions
|
||||
.filterIsInstance<androidx.credentials.GetPublicKeyCredentialOption>()
|
||||
.firstOrNull()?.clientDataHash
|
||||
|
||||
// Show verifying status to user
|
||||
updateLoadingMessage(R.string.passkey_verifying)
|
||||
|
||||
// Verify origin of the calling app
|
||||
val originVerifier = OriginVerifier()
|
||||
val callingAppInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
providerRequest.callingAppInfo
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
// Run origin verification on IO thread
|
||||
val originResult = withContext(Dispatchers.IO) {
|
||||
originVerifier.verifyOrigin(
|
||||
callingAppInfo = callingAppInfo,
|
||||
requestedRpId = passkey.rpId,
|
||||
)
|
||||
}
|
||||
|
||||
val verifiedOrigin: String
|
||||
val isPrivilegedCaller: Boolean
|
||||
|
||||
when (originResult) {
|
||||
is OriginVerifier.OriginResult.Success -> {
|
||||
verifiedOrigin = originResult.origin
|
||||
isPrivilegedCaller = originResult.isPrivileged
|
||||
Log.d(TAG, "Origin verified: $verifiedOrigin (privileged: $isPrivilegedCaller)")
|
||||
}
|
||||
is OriginVerifier.OriginResult.Failure -> {
|
||||
Log.e(TAG, "Origin verification failed: ${originResult.reason}")
|
||||
showError("Security error: ${originResult.reason}")
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
// Show authenticating status to user
|
||||
updateLoadingMessage(R.string.passkey_authenticating)
|
||||
|
||||
// Determine clientDataHash and clientDataJson based on what caller provided
|
||||
val clientDataHash: ByteArray
|
||||
val clientDataJson: String?
|
||||
if (providedClientDataHash != null && isPrivilegedCaller) {
|
||||
// Browser provided clientDataHash - use it directly
|
||||
clientDataHash = providedClientDataHash
|
||||
clientDataJson = null
|
||||
} else {
|
||||
// Native app scenario - build clientDataJSON ourselves
|
||||
val challenge = requestObj.optString("challenge", "")
|
||||
val json = buildClientDataJson(challenge, verifiedOrigin)
|
||||
clientDataHash = sha256(json.toByteArray(Charsets.UTF_8))
|
||||
clientDataJson = json
|
||||
}
|
||||
|
||||
// Use PasskeyAuthenticator.getAssertion for signing
|
||||
val credentialId = PasskeyHelper.guidToBytes(passkey.id.toString())
|
||||
val prfInputs = extractPrfInputs(requestObj)
|
||||
val assertion = PasskeyAuthenticator.getAssertion(
|
||||
credentialId = credentialId,
|
||||
clientDataHash = clientDataHash,
|
||||
rpId = passkey.rpId,
|
||||
privateKeyJWK = passkey.privateKey,
|
||||
userId = passkey.userHandle,
|
||||
uvPerformed = true,
|
||||
prfInputs = prfInputs,
|
||||
prfSecret = passkey.prfKey,
|
||||
)
|
||||
|
||||
// Build response JSON
|
||||
val response = buildPublicKeyCredentialResponse(
|
||||
assertion = assertion,
|
||||
clientDataJson = clientDataJson,
|
||||
)
|
||||
|
||||
val resultIntent = Intent()
|
||||
try {
|
||||
PendingIntentHandler.setGetCredentialResponse(resultIntent, response)
|
||||
setResult(RESULT_OK, resultIntent)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error setting credential response", e)
|
||||
try {
|
||||
PendingIntentHandler.setGetCredentialException(
|
||||
resultIntent,
|
||||
androidx.credentials.exceptions.GetCredentialUnknownException("Failed to generate assertion: ${e.message}"),
|
||||
)
|
||||
setResult(RESULT_OK, resultIntent)
|
||||
} catch (e2: Exception) {
|
||||
Log.e(TAG, "Error setting exception", e2)
|
||||
setResult(RESULT_CANCELED)
|
||||
}
|
||||
}
|
||||
finish()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error processing authentication request", e)
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
finish()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error processing authentication request", e)
|
||||
setResult(RESULT_CANCELED)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -246,8 +246,11 @@ class PasskeyFormFragment : Fragment() {
|
||||
val requestObj = JSONObject(viewModel.requestJson)
|
||||
val challenge = requestObj.optString("challenge", "")
|
||||
|
||||
// Use origin from the request, or fallback to RP id
|
||||
val requestOrigin = viewModel.origin ?: ("https://" + viewModel.rpId)
|
||||
// Use the origin set by PasskeyRegistrationActivity
|
||||
val requestOrigin = viewModel.origin
|
||||
?: throw net.aliasvault.app.exceptions.PasskeyOperationException(
|
||||
"Origin not available",
|
||||
)
|
||||
|
||||
// Extract PRF inputs if present
|
||||
val prfInputs = extractPrfInputs(requestObj)
|
||||
@@ -438,8 +441,9 @@ class PasskeyFormFragment : Fragment() {
|
||||
val requestObj = JSONObject(viewModel.requestJson)
|
||||
val challenge = requestObj.optString("challenge", "")
|
||||
|
||||
// Use origin from the request
|
||||
val requestOrigin = viewModel.origin ?: throw PasskeyOperationException("Origin not available")
|
||||
// Use the origin set by PasskeyRegistrationActivity
|
||||
val requestOrigin = viewModel.origin
|
||||
?: throw PasskeyOperationException("Origin not available")
|
||||
|
||||
// Extract PRF inputs if present
|
||||
val prfInputs = extractPrfInputs(requestObj)
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
package net.aliasvault.app.credentialprovider
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.activity.viewModels
|
||||
import androidx.credentials.CreatePublicKeyCredentialRequest
|
||||
import androidx.credentials.provider.CallingAppInfo
|
||||
import androidx.credentials.provider.PendingIntentHandler
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import net.aliasvault.app.R
|
||||
import net.aliasvault.app.credentialprovider.models.PasskeyRegistrationViewModel
|
||||
import net.aliasvault.app.utils.Helpers
|
||||
@@ -65,10 +71,9 @@ class PasskeyRegistrationActivity : FragmentActivity() {
|
||||
return
|
||||
}
|
||||
|
||||
// Get requestJson, clientDataHash, and origin from the request
|
||||
// Get requestJson, clientDataHash from the request
|
||||
viewModel.requestJson = createRequest.requestJson
|
||||
viewModel.clientDataHash = createRequest.clientDataHash
|
||||
viewModel.origin = createRequest.origin
|
||||
|
||||
// Parse request JSON to extract RP ID and user info
|
||||
val requestObj = JSONObject(viewModel.requestJson)
|
||||
@@ -102,29 +107,18 @@ class PasskeyRegistrationActivity : FragmentActivity() {
|
||||
null
|
||||
}
|
||||
|
||||
// Show loading screen while unlock is in progress
|
||||
// Show loading screen while verification and unlock are in progress
|
||||
setContentView(R.layout.activity_loading)
|
||||
|
||||
// Initialize unlock coordinator
|
||||
unlockCoordinator = UnlockCoordinator(
|
||||
activity = this,
|
||||
vaultStore = vaultStore,
|
||||
onUnlocked = {
|
||||
// Vault unlocked successfully - proceed with passkey registration
|
||||
proceedWithPasskeyRegistration(savedInstanceState)
|
||||
},
|
||||
onCancelled = {
|
||||
// User cancelled unlock
|
||||
finish()
|
||||
},
|
||||
onError = { errorMessage ->
|
||||
// Error during unlock
|
||||
showError(errorMessage)
|
||||
},
|
||||
)
|
||||
// Get calling app info for origin verification
|
||||
val callingAppInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
||||
providerRequest.callingAppInfo
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
// Start the unlock flow
|
||||
unlockCoordinator.startUnlockFlow()
|
||||
// Verify origin and start unlock flow
|
||||
verifyOriginAndStartUnlock(callingAppInfo, savedInstanceState)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error in onCreate", e)
|
||||
finish()
|
||||
@@ -140,6 +134,76 @@ class PasskeyRegistrationActivity : FragmentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the loading message displayed to the user.
|
||||
*/
|
||||
private fun updateLoadingMessage(messageResId: Int) {
|
||||
runOnUiThread {
|
||||
try {
|
||||
findViewById<TextView>(R.id.loadingMessage)?.text = getString(messageResId)
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Could not update loading message", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify origin on background thread and start unlock flow if successful.
|
||||
*/
|
||||
private fun verifyOriginAndStartUnlock(callingAppInfo: CallingAppInfo?, savedInstanceState: Bundle?) {
|
||||
lifecycleScope.launch {
|
||||
try {
|
||||
// Show verifying status to user (network call may happen)
|
||||
updateLoadingMessage(R.string.passkey_verifying)
|
||||
|
||||
// Run origin verification on IO thread (asset links fetch requires network)
|
||||
val originVerifier = OriginVerifier()
|
||||
val originResult = withContext(Dispatchers.IO) {
|
||||
originVerifier.verifyOrigin(
|
||||
callingAppInfo = callingAppInfo,
|
||||
requestedRpId = viewModel.rpId,
|
||||
)
|
||||
}
|
||||
|
||||
when (originResult) {
|
||||
is OriginVerifier.OriginResult.Success -> {
|
||||
viewModel.origin = originResult.origin
|
||||
viewModel.isPrivilegedCaller = originResult.isPrivileged
|
||||
Log.d(TAG, "Origin verified: ${originResult.origin} (privileged: ${originResult.isPrivileged})")
|
||||
|
||||
// Initialize unlock coordinator
|
||||
unlockCoordinator = UnlockCoordinator(
|
||||
activity = this@PasskeyRegistrationActivity,
|
||||
vaultStore = vaultStore,
|
||||
onUnlocked = {
|
||||
// Vault unlocked successfully - proceed with passkey registration
|
||||
proceedWithPasskeyRegistration(savedInstanceState)
|
||||
},
|
||||
onCancelled = {
|
||||
// User cancelled unlock
|
||||
finish()
|
||||
},
|
||||
onError = { errorMessage ->
|
||||
// Error during unlock
|
||||
showError(errorMessage)
|
||||
},
|
||||
)
|
||||
|
||||
// Start the unlock flow
|
||||
unlockCoordinator.startUnlockFlow()
|
||||
}
|
||||
is OriginVerifier.OriginResult.Failure -> {
|
||||
Log.e(TAG, "Origin verification failed: ${originResult.reason}")
|
||||
showError("Security error: ${originResult.reason}")
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error verifying origin", e)
|
||||
showError("Error verifying application: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Proceed with passkey registration after authentication (biometric or PIN).
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,9 @@ class PasskeyRegistrationViewModel : ViewModel() {
|
||||
/** The origin URL of the passkey request. */
|
||||
var origin: String? = null
|
||||
|
||||
/** Whether the caller is a privileged app (browser). */
|
||||
var isPrivilegedCaller: Boolean = false
|
||||
|
||||
/** The relying party identifier. */
|
||||
var rpId: String = ""
|
||||
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">This will replace the existing passkey with a new one. Please be aware that your old passkey will be overwritten and no longer accessible. If you wish to create a separate passkey instead, go back to the previous screen.</string>
|
||||
<string name="passkey_replacing">Replacing passkey…</string>
|
||||
<string name="passkey_checking_connection">Checking connection…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Connection Error</string>
|
||||
<string name="connection_error_message">No connection to the server can be made. Please check your internet connection and try creating the passkey again.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">Dies wird den bestehenden Passkey durch einen neuen ersetzen. Bitte beachte, dass Dein alter Passkey überschrieben wird und nicht mehr zugänglich ist. Wenn Du stattdessen einen separaten Passkey erstellen möchtest, gehe zurück zum vorherigen Schritt.</string>
|
||||
<string name="passkey_replacing">Passkey ersetzen…</string>
|
||||
<string name="passkey_checking_connection">Verbindung wird überprüft…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Verbindungsfehler</string>
|
||||
<string name="connection_error_message">Es kann keine Verbindung zum Server hergestellt werden. Bitte überprüfe Deine Internetverbindung und versuche das Erstellen des Passkeys erneut.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">Esto reemplazará la llave de acceso existente con una nueva. Tenga en cuenta que su llave de acceso antigua será sobrescrita y ya no será accesible. Si desea crear una llave de acceso separada en su lugar, vuelva a la pantalla anterior.</string>
|
||||
<string name="passkey_replacing">Reemplazando llave de acceso…</string>
|
||||
<string name="passkey_checking_connection">Comprobando la conexión…</string>
|
||||
<string name="passkey_retrieving">Recuperando llave…</string>
|
||||
<string name="passkey_verifying">Verificando…</string>
|
||||
<string name="passkey_authenticating">Autenticando…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Error de conexión</string>
|
||||
<string name="connection_error_message">No se puede establecer conexión con el servidor. Por favor, compruebe su conexión a Internet e intente crear la llave de acceso de nuevo.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">Tämä korvaa olemassa olevan todennusavaimen uudella todennusavaimella. Ole hyvä ja ota huomioon, että vanha todennusavaimesi on korvattu eikä enää käytettävissä. Jos haluat luoda erillisen todennusavaimen sen sijaan, mene takaisin edelliseen ruutuun.</string>
|
||||
<string name="passkey_replacing">Korvataan todennusavainta...</string>
|
||||
<string name="passkey_checking_connection">Tarkistetaan yhteyttä</string>
|
||||
<string name="passkey_retrieving">Noudetaan todennusavainta...</string>
|
||||
<string name="passkey_verifying">Tarkistetaan…</string>
|
||||
<string name="passkey_authenticating">Todennetaan…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Yhteysvirhe</string>
|
||||
<string name="connection_error_message">Yhteyttä palvelimeen ei voida luoda. Tarkista internet-yhteytesi ja yritä luoda todennusavain uudelleen.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">This will replace the existing passkey with a new one. Please be aware that your old passkey will be overwritten and no longer accessible. If you wish to create a separate passkey instead, go back to the previous screen.</string>
|
||||
<string name="passkey_replacing">Replacing passkey…</string>
|
||||
<string name="passkey_checking_connection">Checking connection…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Connection Error</string>
|
||||
<string name="connection_error_message">No connection to the server can be made. Please check your internet connection and try creating the passkey again.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">This will replace the existing passkey with a new one. Please be aware that your old passkey will be overwritten and no longer accessible. If you wish to create a separate passkey instead, go back to the previous screen.</string>
|
||||
<string name="passkey_replacing">Replacing passkey…</string>
|
||||
<string name="passkey_checking_connection">החיבור נבדק…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">שגיאת חיבור</string>
|
||||
<string name="connection_error_message">No connection to the server can be made. Please check your internet connection and try creating the passkey again.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">Questo sostituirà la passkey esistente con una nuova. Si prega di notare che la vecchia passkey sarà sovrascritta e non sarà più accessibile. Se si desidera invece creare una passkey separata, tornare alla schermata precedente.</string>
|
||||
<string name="passkey_replacing">Sostituzione passkey…</string>
|
||||
<string name="passkey_checking_connection">Controllo connessione…</string>
|
||||
<string name="passkey_retrieving">Recupero passkey…</string>
|
||||
<string name="passkey_verifying">Verifica…</string>
|
||||
<string name="passkey_authenticating">Autenticazione…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Errore Di Connessione</string>
|
||||
<string name="connection_error_message">Non è possibile effettuare alcuna connessione al server. Controlla la tua connessione internet e prova a creare nuovamente la passkey.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">Dit zal de bestaande passkey vervangen door een nieuwe. Houd er rekening mee dat je oude passkey wordt overschreven en niet langer toegankelijk is. Als je in plaats hiervan een aparte passkey wilt maken, ga dan terug naar het vorige scherm.</string>
|
||||
<string name="passkey_replacing">Passkey vervangen…</string>
|
||||
<string name="passkey_checking_connection">Verbinding controleren…</string>
|
||||
<string name="passkey_retrieving">Passkey ophalen…</string>
|
||||
<string name="passkey_verifying">Verifiëren…</string>
|
||||
<string name="passkey_authenticating">Authenticeren…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Verbindingsfout</string>
|
||||
<string name="connection_error_message">Er kan geen verbinding met de server worden gemaakt. Controleer je internetverbinding en probeer het opnieuw.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">Spowoduje to zastąpienie dotychczasowego klucza dostępu nowym. Należy pamiętać, że stare klucz zostanie nadpisany i nie będzie już dostępny. Jeśli chcesz utworzyć nowy klucz dostępu, wróć do poprzedniego ekranu.</string>
|
||||
<string name="passkey_replacing">Zastępowanie klucza dostępu…</string>
|
||||
<string name="passkey_checking_connection">Sprawdzanie połączenia…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Błąd połączenia</string>
|
||||
<string name="connection_error_message">Nie można nawiązać połączenia z serwerem. Sprawdź połączenie internetowe i spróbuj ponownie utworzyć klucz dostępu.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">Isto irá substituir a passkey existente com uma nova. Por favor, saiba que sua passkey anterior será sobrescrita e não será mais acessível. Se você deseja criar uma passkey separadamente, volte à tela anterior.</string>
|
||||
<string name="passkey_replacing">Substituindo passkey…</string>
|
||||
<string name="passkey_checking_connection">Verificando conexão…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Erro de Conexão</string>
|
||||
<string name="connection_error_message">A conexão com o servidor não foi feita. Por favor, confira sua conexão com a internet e tente criar a passkey novamente.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">Существующий ключ доступа будет заменен на новый. Обратите внимание, что старый ключ будет перезаписан и станет недоступен. Если вы хотите создать отдельный ключ доступа, вернитесь на предыдущий экран.</string>
|
||||
<string name="passkey_replacing">Замена ключа доступа…</string>
|
||||
<string name="passkey_checking_connection">Проверка соединения…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Ошибка подключения</string>
|
||||
<string name="connection_error_message">Не удалось подключиться к серверу. Проверьте интернет-соединение и попробуйте создать ключ доступа снова.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">This will replace the existing passkey with a new one. Please be aware that your old passkey will be overwritten and no longer accessible. If you wish to create a separate passkey instead, go back to the previous screen.</string>
|
||||
<string name="passkey_replacing">Replacing passkey…</string>
|
||||
<string name="passkey_checking_connection">Checking connection…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Connection Error</string>
|
||||
<string name="connection_error_message">No connection to the server can be made. Please check your internet connection and try creating the passkey again.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">This will replace the existing passkey with a new one. Please be aware that your old passkey will be overwritten and no longer accessible. If you wish to create a separate passkey instead, go back to the previous screen.</string>
|
||||
<string name="passkey_replacing">Replacing passkey…</string>
|
||||
<string name="passkey_checking_connection">Checking connection…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Connection Error</string>
|
||||
<string name="connection_error_message">No connection to the server can be made. Please check your internet connection and try creating the passkey again.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">This will replace the existing passkey with a new one. Please be aware that your old passkey will be overwritten and no longer accessible. If you wish to create a separate passkey instead, go back to the previous screen.</string>
|
||||
<string name="passkey_replacing">Replacing passkey…</string>
|
||||
<string name="passkey_checking_connection">Checking connection…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Connection Error</string>
|
||||
<string name="connection_error_message">No connection to the server can be made. Please check your internet connection and try creating the passkey again.</string>
|
||||
|
||||
@@ -43,6 +43,9 @@
|
||||
<string name="passkey_replace_explanation">This will replace the existing passkey with a new one. Please be aware that your old passkey will be overwritten and no longer accessible. If you wish to create a separate passkey instead, go back to the previous screen.</string>
|
||||
<string name="passkey_replacing">正在替换通行密钥…</string>
|
||||
<string name="passkey_checking_connection">检查连接中…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">连接错误</string>
|
||||
<string name="connection_error_message">No connection to the server can be made. Please check your internet connection and try creating the passkey again.</string>
|
||||
|
||||
@@ -49,6 +49,9 @@
|
||||
<string name="passkey_replace_explanation">This will replace the existing passkey with a new one. Please be aware that your old passkey will be overwritten and no longer accessible. If you wish to create a separate passkey instead, go back to the previous screen.</string>
|
||||
<string name="passkey_replacing">Replacing passkey…</string>
|
||||
<string name="passkey_checking_connection">Checking connection…</string>
|
||||
<string name="passkey_retrieving">Retrieving passkey…</string>
|
||||
<string name="passkey_verifying">Verifying…</string>
|
||||
<string name="passkey_authenticating">Authenticating…</string>
|
||||
|
||||
<!-- Vault sync error messages -->
|
||||
<string name="connection_error_title">Connection Error</string>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"common": {
|
||||
"cancel": "Annuler",
|
||||
"close": "Close",
|
||||
"close": "Fermer",
|
||||
"delete": "Supprimer",
|
||||
"save": "Sauvegarder",
|
||||
"yes": "Oui",
|
||||
@@ -24,78 +24,78 @@
|
||||
"deleteItemConfirmTitle": "Supprimer l'élement",
|
||||
"deleteItemConfirmDescription": "Êtes-vous certain de vouloir supprimer cet élément?",
|
||||
"errors": {
|
||||
"unknownError": "An unknown error occurred. Please try again.",
|
||||
"unknownErrorTryAgain": "An unknown error occurred. Please try again.",
|
||||
"serverVersionTooOld": "The AliasVault server needs to be updated to a newer version in order to use this feature. Please contact the server admin if you need help."
|
||||
"unknownError": "Une erreur inconnue s'est produite. Merci de réessayer.",
|
||||
"unknownErrorTryAgain": "Une erreur inconnue s'est produite. Merci de réessayer.",
|
||||
"serverVersionTooOld": "Le serveur AliasVault doit être mis à jour vers une version plus récente pour pouvoir utiliser cette fonctionnalité. Veuillez contacter l'administrateur du serveur si vous avez besoin d'aide."
|
||||
}
|
||||
},
|
||||
"auth": {
|
||||
"login": "Log in",
|
||||
"logout": "Logout",
|
||||
"username": "Username or email",
|
||||
"password": "Password",
|
||||
"authCode": "Authentication Code",
|
||||
"unlock": "Unlock",
|
||||
"unlocking": "Unlocking...",
|
||||
"loggingIn": "Logging in",
|
||||
"validatingCredentials": "Validating credentials",
|
||||
"syncingVault": "Syncing vault",
|
||||
"verifyingAuthCode": "Verifying authentication code",
|
||||
"verify": "Verify",
|
||||
"unlockVault": "Unlock Vault",
|
||||
"unlockWithPin": "Unlock with PIN",
|
||||
"enterPassword": "Enter your password to unlock your vault",
|
||||
"enterPasswordPlaceholder": "Password",
|
||||
"enterAuthCode": "Enter 6-digit code",
|
||||
"login": "Se connecter",
|
||||
"logout": "Se déconnecter",
|
||||
"username": "Nom d'utilisateur ou email",
|
||||
"password": "Mot de passe",
|
||||
"authCode": "Code d'authentification",
|
||||
"unlock": "Déverrouiller",
|
||||
"unlocking": "Déverrouillage...",
|
||||
"loggingIn": "Connexion en cours",
|
||||
"validatingCredentials": "Validation des identifiants",
|
||||
"syncingVault": "Synchronisation du coffre",
|
||||
"verifyingAuthCode": "Vérification du code d'authentification",
|
||||
"verify": "Vérifier",
|
||||
"unlockVault": "Déverrouiller le coffre",
|
||||
"unlockWithPin": "Déverrouiller avec un code PIN",
|
||||
"enterPassword": "Entrez votre mot de passe principal pour déverrouiller votre coffre-fort",
|
||||
"enterPasswordPlaceholder": "Mot de passe",
|
||||
"enterAuthCode": "Saisissez le code à 6 chiffres",
|
||||
"usernamePlaceholder": "nom / nom@entreprise.com",
|
||||
"passwordPlaceholder": "Enter your password",
|
||||
"enableBiometric": "Enable {{biometric}}?",
|
||||
"biometricPrompt": "Would you like to use {{biometric}} to unlock your vault?",
|
||||
"tryBiometricAgain": "Try {{biometric}} Again",
|
||||
"tryPinAgain": "Try PIN Again",
|
||||
"authCodeNote": "Note: if you don't have access to your authenticator device, you can reset your 2FA with a recovery code by logging in via the website.",
|
||||
"passwordPlaceholder": "Saisissez votre mot de passe",
|
||||
"enableBiometric": "Activer {{biometric}}?",
|
||||
"biometricPrompt": "Voulez-vous utiliser {{biometric}} pour déverrouiller votre coffre ?",
|
||||
"tryBiometricAgain": "Réessayez {{biometric}}",
|
||||
"tryPinAgain": "Réessayez le PIN",
|
||||
"authCodeNote": "Remarque : si vous n'avez pas accès à votre appareil d'authentification, vous pouvez réinitialiser votre authentification 2FA avec un code de récupération en vous connectant via le site web.",
|
||||
"errors": {
|
||||
"credentialsRequired": "Username and password are required",
|
||||
"invalidAuthCode": "Please enter a valid 6-digit authentication code",
|
||||
"credentialsRequired": "Le nom d'utilisateur et le mot de passe sont requis",
|
||||
"invalidAuthCode": "Veuillez entrer un code d'authentification à 6 chiffres valide",
|
||||
"incorrectPassword": "Mot de passe incorrect. Veuillez réessayer.",
|
||||
"enterPassword": "Please enter your password",
|
||||
"serverError": "Could not reach AliasVault server. Please try again later or contact support if the problem persists.",
|
||||
"serverErrorSelfHosted": "Could not reach the API. For self-hosted instances, please verify the API endpoint is reachable by navigating to it in a browser: it should display 'OK'.",
|
||||
"networkError": "Network request failed. Please check your internet connection and try again.",
|
||||
"networkErrorSelfHosted": "Network request failed. Check your network connection and server availability. For self-hosted instances, please ensure you have a valid SSL certificate installed. Self-signed certificates are not supported on mobile devices for security reasons.",
|
||||
"sessionExpired": "Your session has expired. Please login again.",
|
||||
"httpError": "HTTP error: {{status}}"
|
||||
"enterPassword": "Veuillez saisir votre mot de passe",
|
||||
"serverError": "Impossible d'accéder au serveur AliasVault. Veuillez réessayer plus tard ou contacter le support si le problème persiste.",
|
||||
"serverErrorSelfHosted": "Impossible d'atteindre l'API. Pour les instances auto-hébergées, veuillez vérifier que le point de terminaison de l'API est accessible en naviguant vers celui-ci dans un navigateur : il devrait afficher 'OK'.",
|
||||
"networkError": "Erreur réseau. Vérifiez votre connexion et réessayez.",
|
||||
"networkErrorSelfHosted": "La requête réseau a échoué. Vérifiez votre connexion réseau et la disponibilité du serveur. Pour les instances auto-hébergées, veuillez vous assurer que vous avez un certificat SSL valide. Les certificats auto-signés ne sont pas pris en charge pour des raisons de sécurité.",
|
||||
"sessionExpired": "Votre session a expiré. Veuillez vous reconnecter.",
|
||||
"httpError": "Erreur HTTP : {{status}}"
|
||||
},
|
||||
"confirmLogout": "Are you sure you want to logout? You need to login again with your master password to access your vault.",
|
||||
"confirmLogout": "Êtes-vous sûr de vouloir vous déconnecter ? Vous devez vous reconnecter avec votre mot de passe maître pour accéder à votre coffre.",
|
||||
"noAccountYet": "Pas encore de compte ?",
|
||||
"createNewVault": "Créer un nouveau coffre-fort",
|
||||
"connectingTo": "Connexion à",
|
||||
"loggedInAs": "Connecté en tant que"
|
||||
},
|
||||
"vault": {
|
||||
"syncingVault": "Syncing vault",
|
||||
"uploadingVaultToServer": "Uploading vault to server",
|
||||
"savingChangesToVault": "Saving changes to vault",
|
||||
"checkingForVaultUpdates": "Checking for vault updates",
|
||||
"executingOperation": "Executing operation...",
|
||||
"checkingVaultUpdates": "Checking vault updates",
|
||||
"syncingUpdatedVault": "Syncing updated vault",
|
||||
"syncingVault": "Synchronisation du coffre",
|
||||
"uploadingVaultToServer": "Envoi du coffre sur le serveur",
|
||||
"savingChangesToVault": "Enregistrement des modifications dans le coffre",
|
||||
"checkingForVaultUpdates": "Vérification des mises à jour du coffre",
|
||||
"executingOperation": "Exécution de l'opération...",
|
||||
"checkingVaultUpdates": "Vérification des mises à jour du coffre",
|
||||
"syncingUpdatedVault": "Synchronisation du coffre mis à jour",
|
||||
"errors": {
|
||||
"failedToGetEncryptedDatabase": "Failed to get encrypted database",
|
||||
"usernameNotFound": "Username not found",
|
||||
"vaultOutdated": "Your vault is outdated. Please login on the AliasVault website and follow the steps.",
|
||||
"failedToSyncVault": "Failed to sync vault",
|
||||
"versionNotSupported": "This version of the AliasVault mobile app is not supported by the server anymore. Please update your app to the latest version.",
|
||||
"serverVersionNotSupported": "The AliasVault server needs to be updated to a newer version in order to use this mobile app. Please contact support if you need help.",
|
||||
"failedToGetEncryptedDatabase": "Impossible d'obtenir la base de données chiffrée",
|
||||
"usernameNotFound": "Identifiant non trouvé",
|
||||
"vaultOutdated": "Votre coffre est obsolète. Veuillez vous connecter sur le site AliasVault et suivre les étapes.",
|
||||
"failedToSyncVault": "Échec de la synchronisation du coffre",
|
||||
"versionNotSupported": "Cette version de l'application mobile AliasVault n'est plus prise en charge par le serveur. Veuillez mettre à jour votre application vers la dernière version.",
|
||||
"serverVersionNotSupported": "Le serveur AliasVault doit être mis à jour vers une version plus récente afin d'utiliser cette application mobile. Veuillez contacter le support si vous avez besoin d'aide.",
|
||||
"appOutdated": "Cette application est obsolète et ne peut pas être utilisée pour accéder à cette (nouvelle) version du coffre. Veuillez mettre à jour l'application AliasVault pour continuer.",
|
||||
"passwordChanged": "Your password has changed since the last time you logged in. Please login again for security reasons."
|
||||
"passwordChanged": "Votre mot de passe a changé depuis la dernière fois que vous vous êtes connecté. Veuillez vous reconnecter pour des raisons de sécurité."
|
||||
}
|
||||
},
|
||||
"credentials": {
|
||||
"title": "Credentials",
|
||||
"addCredential": "Add Credential",
|
||||
"editCredential": "Edit Credential",
|
||||
"deleteCredential": "Delete Credential",
|
||||
"title": "Identifiants",
|
||||
"addCredential": "Ajouter un identifiant",
|
||||
"editCredential": "Modifier l'identifiant",
|
||||
"deleteCredential": "Supprimer l'identifiant",
|
||||
"deleteConfirm": "Êtes-vous sûr de vouloir supprimer ces identifiants ? Cette action est irréversible.",
|
||||
"service": "Service",
|
||||
"serviceName": "Nom du service",
|
||||
@@ -113,116 +113,116 @@
|
||||
"birthDate": "Date de naissance",
|
||||
"birthDatePlaceholder": "AAAA-MM-JJ",
|
||||
"notes": "Notes",
|
||||
"randomAlias": "Random Alias",
|
||||
"manual": "Manual",
|
||||
"generateRandomAlias": "Generate Random Alias",
|
||||
"clearAliasFields": "Clear Alias Fields",
|
||||
"enterFullEmail": "Enter full email address",
|
||||
"enterEmailPrefix": "Enter email prefix",
|
||||
"useDomainChooser": "Use domain chooser",
|
||||
"enterCustomDomain": "Enter custom domain",
|
||||
"selectEmailDomain": "Select Email Domain",
|
||||
"privateEmailTitle": "Private Email",
|
||||
"privateEmailAliasVaultServer": "AliasVault server",
|
||||
"privateEmailDescription": "E2E encrypted, fully private.",
|
||||
"publicEmailTitle": "Public Temp Email Providers",
|
||||
"publicEmailDescription": "Anonymous but limited privacy. Email content is readable by anyone that knows the address.",
|
||||
"searchPlaceholder": "Search vault...",
|
||||
"noMatchingCredentials": "No matching credentials found",
|
||||
"noCredentialsFound": "No credentials found. Create one to get started. Tip: you can also login to the AliasVault web app to import credentials from other password managers.",
|
||||
"noPasskeysFound": "No passkeys have been created yet. Passkeys are created by visiting a website that offers passkeys as an authentication method.",
|
||||
"noAttachmentsFound": "No credentials with attachments found",
|
||||
"recentEmails": "Recent emails",
|
||||
"loadingEmails": "Loading emails...",
|
||||
"noEmailsYet": "No emails received yet.",
|
||||
"offlineEmailsMessage": "You are offline. Please connect to the internet to load your emails.",
|
||||
"emailLoadError": "An error occurred while loading emails. Please try again later.",
|
||||
"emailUnexpectedError": "An unexpected error occurred while loading emails. Please try again later.",
|
||||
"password": "Password",
|
||||
"passwordLength": "Password Length",
|
||||
"changePasswordComplexity": "Password Settings",
|
||||
"includeLowercase": "Lowercase (a-z)",
|
||||
"includeUppercase": "Uppercase (A-Z)",
|
||||
"includeNumbers": "Numbers (0-9)",
|
||||
"includeSpecialChars": "Special Characters (!@#)",
|
||||
"avoidAmbiguousChars": "Avoid Ambiguous Characters",
|
||||
"deletingCredential": "Deleting credential...",
|
||||
"errorLoadingCredentials": "Error loading credentials",
|
||||
"vaultSyncFailed": "Vault sync failed",
|
||||
"vaultSyncedSuccessfully": "Vault synced successfully",
|
||||
"vaultUpToDate": "Vault is up-to-date",
|
||||
"offlineMessage": "You are offline. Please connect to the internet to sync your vault.",
|
||||
"credentialCreated": "Credential Created!",
|
||||
"credentialCreatedMessage": "Your new credential has been added to your vault and is ready to use.",
|
||||
"credentialDetails": "Credential Details",
|
||||
"emailPreview": "Email Preview",
|
||||
"switchBackToBrowser": "Switch back to your browser to continue.",
|
||||
"randomAlias": "Alias aléatoire",
|
||||
"manual": "Manuel",
|
||||
"generateRandomAlias": "Générer un alias aléatoire",
|
||||
"clearAliasFields": "Effacer les champs d'alias",
|
||||
"enterFullEmail": "Entrez l'adresse email complète",
|
||||
"enterEmailPrefix": "Entrez le préfixe de l'email",
|
||||
"useDomainChooser": "Utiliser le sélecteur de domaine",
|
||||
"enterCustomDomain": "Entrez le domaine personnalisé",
|
||||
"selectEmailDomain": "Sélectionner un domaine de messagerie",
|
||||
"privateEmailTitle": "E-mail privé",
|
||||
"privateEmailAliasVaultServer": "Serveur AliasVault",
|
||||
"privateEmailDescription": "E2E chiffré, entièrement privé.",
|
||||
"publicEmailTitle": "Fournisseurs d'email public temporaires",
|
||||
"publicEmailDescription": "Anonyme mais confidentiel limitée. Le contenu des e-mails est lisible par toute personne qui connaît l'adresse.",
|
||||
"searchPlaceholder": "Rechercher dans le coffre...",
|
||||
"noMatchingCredentials": "Aucun identifiant correspondant trouvé",
|
||||
"noCredentialsFound": "Aucun identifiant trouvé. Créez en un pour commencer. Astuce : vous pouvez également vous connecter à l'application web AliasVault pour importer les identifiants depuis d'autres gestionnaires de mots de passe.",
|
||||
"noPasskeysFound": "Aucune clé d'accès n'a encore été créée. Les clés d'accès sont créés en visitant un site Web qui propose des clés d'accès comme méthode d'authentification.",
|
||||
"noAttachmentsFound": "Aucun identifiant avec des pièces jointes trouvé",
|
||||
"recentEmails": "E-mails récents",
|
||||
"loadingEmails": "Chargement des e-mails...",
|
||||
"noEmailsYet": "Pas encore d'e-mails reçus.",
|
||||
"offlineEmailsMessage": "Vous êtes déconnecté. Veuillez vous connecter à internet pour charger vos e-mails.",
|
||||
"emailLoadError": "Une erreur s'est produite lors du chargement des e-mails. Veuillez réessayer plus tard.",
|
||||
"emailUnexpectedError": "Une erreur inattendue s'est produite lors du chargement des e-mails. Veuillez réessayer plus tard.",
|
||||
"password": "Mot de passe",
|
||||
"passwordLength": "Longueur du mot de passe",
|
||||
"changePasswordComplexity": "Paramètres du mot de passe",
|
||||
"includeLowercase": "Minuscules (a-z)",
|
||||
"includeUppercase": "Majuscules (A-Z)",
|
||||
"includeNumbers": "Nombres (0-9)",
|
||||
"includeSpecialChars": "Caractères spéciaux (!@#)",
|
||||
"avoidAmbiguousChars": "Éviter les caractères ambigus",
|
||||
"deletingCredential": "Suppression de l'identifiant...",
|
||||
"errorLoadingCredentials": "Erreur lors du chargement des identifiants",
|
||||
"vaultSyncFailed": "Échec de la synchronisation du coffre",
|
||||
"vaultSyncedSuccessfully": "Le coffre a été synchronisé avec succès",
|
||||
"vaultUpToDate": "Le coffre est à jour",
|
||||
"offlineMessage": "Vous êtes déconnecté. Veuillez vous connecter à internet pour synchroniser votre coffre.",
|
||||
"credentialCreated": "Identifiant créé!",
|
||||
"credentialCreatedMessage": "Votre nouvel identifiant a été ajouté à votre coffre et est prêt à être utilisé.",
|
||||
"credentialDetails": "Détails de l'identifiant",
|
||||
"emailPreview": "Aperçu de l'e-mail",
|
||||
"switchBackToBrowser": "Passez à votre navigateur pour continuer.",
|
||||
"filters": {
|
||||
"all": "(All) Credentials",
|
||||
"passkeys": "Passkeys",
|
||||
"aliases": "Aliases",
|
||||
"userpass": "Passwords",
|
||||
"attachments": "Attachments"
|
||||
"all": "(Tous les) Identifiants de connexion",
|
||||
"passkeys": "Clés d'accès",
|
||||
"aliases": "Alias",
|
||||
"userpass": "Mots de passe",
|
||||
"attachments": "Pièces jointes"
|
||||
},
|
||||
"twoFactorAuth": "Two-factor authentication",
|
||||
"totpCode": "TOTP Code",
|
||||
"attachments": "Attachments",
|
||||
"deleteAttachment": "Delete",
|
||||
"fileSavedTo": "File saved to",
|
||||
"previewNotSupported": "Preview not supported",
|
||||
"downloadToView": "Download the file to view it",
|
||||
"twoFactorAuth": "Authentification à deux facteurs",
|
||||
"totpCode": "Code à usage unique",
|
||||
"attachments": "Pièces jointes",
|
||||
"deleteAttachment": "Supprimer",
|
||||
"fileSavedTo": "Fichier enregistré sous",
|
||||
"previewNotSupported": "Aperçu non supporté",
|
||||
"downloadToView": "Télécharger le fichier pour le voir",
|
||||
"unsavedChanges": {
|
||||
"title": "Discard Changes?",
|
||||
"message": "You have unsaved changes. Are you sure you want to discard them?",
|
||||
"discard": "Discard"
|
||||
"title": "Annuler les modifications?",
|
||||
"message": "Vos modifications n'ont pas été enregistrées. Voulez-vous vraiment les ignorer ?",
|
||||
"discard": "Ignorer"
|
||||
},
|
||||
"toasts": {
|
||||
"credentialUpdated": "Credential updated successfully",
|
||||
"credentialCreated": "Credential created successfully",
|
||||
"credentialDeleted": "Credential deleted successfully",
|
||||
"usernameCopied": "Username copied to clipboard",
|
||||
"emailCopied": "Email copied to clipboard",
|
||||
"passwordCopied": "Password copied to clipboard"
|
||||
"credentialUpdated": "Identifiant mis à jour avec succès",
|
||||
"credentialCreated": "Identifiant créé avec succès",
|
||||
"credentialDeleted": "Identifiant supprimé avec succès",
|
||||
"usernameCopied": "Nom d'utilisateur copié dans le presse-papiers",
|
||||
"emailCopied": "E-mail copié dans le presse-papiers",
|
||||
"passwordCopied": "Mot de passe copié dans le presse-papiers"
|
||||
},
|
||||
"createNewAliasFor": "Create new alias for",
|
||||
"createNewAliasFor": "Créer un nouvel alias pour",
|
||||
"errors": {
|
||||
"loadFailed": "Failed to load credential",
|
||||
"saveFailed": "Failed to save credential"
|
||||
"loadFailed": "Échec du chargement de l'identifiant",
|
||||
"saveFailed": "Échec de l'enregistrement de l'identifiant"
|
||||
},
|
||||
"contextMenu": {
|
||||
"title": "Credential Options",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"copyUsername": "Copy Username",
|
||||
"copyEmail": "Copy Email",
|
||||
"copyPassword": "Copy Password"
|
||||
"title": "Options de l'identifiant",
|
||||
"edit": "Modifier",
|
||||
"delete": "Supprimer",
|
||||
"copyUsername": "Copier le nom d'utilisateur",
|
||||
"copyEmail": "Copier l'e-mail",
|
||||
"copyPassword": "Copier le mot de passe"
|
||||
}
|
||||
},
|
||||
"passkeys": {
|
||||
"passkey": "Passkey",
|
||||
"passkey": "Clé d'identification",
|
||||
"site": "Site",
|
||||
"displayName": "Display Name",
|
||||
"helpText": "Passkeys are created on the website when prompted. They cannot be manually edited. To remove this passkey, you can delete it from this credential.",
|
||||
"passkeyMarkedForDeletion": "Passkey marked for deletion",
|
||||
"passkeyWillBeDeleted": "This passkey will be deleted when you save this credential."
|
||||
"displayName": "Nom affiché",
|
||||
"helpText": "Les clés d'accès sont créées sur le site Web lorsque vous y êtes invité. Elles ne peuvent pas être modifiées manuellement. Pour supprimer cette clé, vous pouvez la supprimer de cet identifiant.",
|
||||
"passkeyMarkedForDeletion": "Clé d'accès marquée pour suppression",
|
||||
"passkeyWillBeDeleted": "Cette clé d'accès sera supprimée lorsque vous enregistrerez cet identifiant."
|
||||
},
|
||||
"totp": {
|
||||
"addCode": "Add 2FA Code",
|
||||
"nameOptional": "Name (optional)",
|
||||
"secretKey": "Secret Key",
|
||||
"instructions": "Enter the secret key shown by the website where you want to add two-factor authentication.",
|
||||
"saveToViewCode": "Save to view code",
|
||||
"addCode": "Ajouter un code 2FA",
|
||||
"nameOptional": "Nom (facultatif)",
|
||||
"secretKey": "Clé secrète",
|
||||
"instructions": "Entrez la clé secrète affichée par le site Web où vous souhaitez ajouter l'authentification à deux facteurs.",
|
||||
"saveToViewCode": "Enregistrer pour afficher le code",
|
||||
"errors": {
|
||||
"invalidSecretKey": "Invalid secret key format."
|
||||
"invalidSecretKey": "Format de clé secrète invalide."
|
||||
}
|
||||
},
|
||||
"settings": {
|
||||
"title": "Settings",
|
||||
"autofill": "Autofill & Passkeys",
|
||||
"title": "Réglages",
|
||||
"autofill": "Remplissage automatique et clés d'accès",
|
||||
"iosAutofillSettings": {
|
||||
"headerText": "You can configure AliasVault to provide native password and passkey autofill functionality in iOS. Follow the instructions below to enable it.",
|
||||
"passkeyNotice": "Passkeys are created through iOS. To store them in AliasVault, ensure Autofill below is enabled.",
|
||||
"howToEnable": "How to enable Autofill & Passkeys:",
|
||||
"headerText": "Vous pouvez configurer AliasVault pour fournir des fonctionnalités natives de saisie automatique du mot de passe et de clés d'accès dans iOS. Suivez les instructions ci-dessous pour l'activer.",
|
||||
"passkeyNotice": "Les clés d'accès sont créées via iOS. Pour les stocker dans AliasVault, assurez-vous que le remplissage automatique ci-dessous est activé.",
|
||||
"howToEnable": "Comment activer le remplissage automatique et les clés d'accès :",
|
||||
"step1": "1. Ouvrir les paramètres d'iOS via le bouton ci-dessous",
|
||||
"step2": "2. Aller dans « Général »",
|
||||
"step3": "3. Appuyer sur « Remplissage automatique et mots de passe »",
|
||||
@@ -233,45 +233,45 @@
|
||||
"warningText": "Note : Vous devrez vous authentifier avec Face ID/Touch ID ou votre code d'accès lorsque vous utilisez le remplissage automatique."
|
||||
},
|
||||
"androidAutofillSettings": {
|
||||
"warningTitle": "⚠️ Experimental Feature",
|
||||
"warningDescription": "Autofill and passkey support for Android is currently in an experimental state.",
|
||||
"warningLink": "Read more about it here",
|
||||
"headerText": "You can configure AliasVault to provide native password and passkey autofill functionality in Android. Follow the instructions below to enable it.",
|
||||
"passkeyNotice": "Passkeys are created through Android Credential Manager (Android 14+). To store them in AliasVault, ensure Autofill below is enabled.",
|
||||
"howToEnable": "How to enable Autofill & Passkeys:",
|
||||
"step1": "1. Open Android Settings via the button below, and change the \"autofill preferred service\" to \"AliasVault\"",
|
||||
"openAutofillSettings": "Open Autofill Settings",
|
||||
"buttonTip": "If the button above doesn't work it might be blocked because of security settings. You can manually go to Android Settings → General Management → Passwords and autofill.",
|
||||
"step2": "2. Some apps, e.g. Google Chrome, may require manual configuration in their settings to allow third-party autofill apps. However, most apps should work with autofill by default.",
|
||||
"alreadyConfigured": "I already configured it",
|
||||
"advancedOptions": "Advanced Options",
|
||||
"showSearchText": "Show search text",
|
||||
"showSearchTextDescription": "Include the text AliasVault receives from Android that it uses to search for a matching credential"
|
||||
"warningTitle": "⚠️ Fonctionnalité expérimentale",
|
||||
"warningDescription": "Le remplissage automatique et la prise en charge de la clé d'accès pour Android sont actuellement dans un état expérimental.",
|
||||
"warningLink": "En savoir plus à ce sujet ici",
|
||||
"headerText": "Vous pouvez configurer AliasVault pour fournir le mot de passe natif et la fonctionnalité de saisie automatique du mot de passe dans Android. Suivez les instructions ci-dessous pour l'activer.",
|
||||
"passkeyNotice": "Les mots de passe sont créés via le gestionnaire d'identifiants Android (Android 14+). Pour les stocker dans AliasVault, assurez-vous que le remplissage automatique ci-dessous est activé.",
|
||||
"howToEnable": "Comment activer le remplissage automatique et les clés d'accès:",
|
||||
"step1": "1. Ouvrez les paramètres Android via le bouton ci-dessous, et changez le \"service préféré de saisie automatique\" par \"AliasVault\"",
|
||||
"openAutofillSettings": "Ouvrir les paramètres de remplissage automatique",
|
||||
"buttonTip": "Si le bouton ci-dessus ne fonctionne pas, il peut être bloqué en raison des paramètres de sécurité. Vous pouvez manuellement aller dans Réglages Android → Gestion Générale → Mots de passe et saisie automatique.",
|
||||
"step2": "2. Certaines applications, par exemple Google Chrome, peuvent nécessiter une configuration manuelle dans leurs paramètres pour permettre le remplissage automatique des applications tierces. Cependant, la plupart des applications devraient fonctionner avec le remplissage automatique par défaut.",
|
||||
"alreadyConfigured": "Je l'ai déjà configuré",
|
||||
"advancedOptions": "Options avancées",
|
||||
"showSearchText": "Afficher le texte de recherche",
|
||||
"showSearchTextDescription": "Inclure le texte que AliasVault reçoit d'Android qu'il utilise pour rechercher un identifiant correspondant"
|
||||
},
|
||||
"vaultUnlock": "Méthode de déverrouillage du coffre-fort",
|
||||
"autoLock": "Délai de verrouillage automatique",
|
||||
"clipboardClear": "Clear Clipboard",
|
||||
"clipboardClearDescription": "Automatically clear copied passwords and sensitive information from your clipboard after a specified time period.",
|
||||
"clipboardClearAndroidWarning": "Note: some Android devices have clipboard history enabled, which may keep track of previously copied items, even after AliasVault clears the clipboard. AliasVault can only overwrite the most recent item, but older entries may remain visible in history. For security reasons, we recommend disabling any clipboard history features in your device settings.",
|
||||
"clipboardClear": "Effacer le presse-papiers",
|
||||
"clipboardClearDescription": "Effacer automatiquement les mots de passe copiés et les informations sensibles du presse-papiers après une période donnée.",
|
||||
"clipboardClearAndroidWarning": "Remarque : certains appareils Android ont l'historique du presse-papiers activé, qui peut garder une trace des éléments précédemment copiés, même après que AliasVault a effacé le presse-papiers. AliasVault ne peut que remplacer l'élément le plus récent, mais les entrées plus anciennes peuvent rester visibles dans l'historique. Pour des raisons de sécurité, nous vous recommandons de désactiver toutes les fonctionnalités d'historique du presse-papiers dans les paramètres de votre appareil.",
|
||||
"clipboardClearOptions": {
|
||||
"never": "Never",
|
||||
"5seconds": "5 seconds",
|
||||
"10seconds": "10 seconds",
|
||||
"15seconds": "15 seconds",
|
||||
"30seconds": "30 seconds"
|
||||
"never": "Jamais",
|
||||
"5seconds": "5 secondes",
|
||||
"10seconds": "10 secondes",
|
||||
"15seconds": "15 secondes",
|
||||
"30seconds": "30 secondes"
|
||||
},
|
||||
"batteryOptimizationHelpTitle": "Enable Background Clipboard Clearing",
|
||||
"batteryOptimizationActive": "Battery optimization is blocking background tasks",
|
||||
"batteryOptimizationDisabled": "Background clipboard clearing enabled",
|
||||
"batteryOptimizationHelpDescription": "Android's battery optimization prevents reliable clipboard clearing when the app is in the background. Disabling battery optimization for AliasVault allows precise background clipboard clearing and automatically grants necessary alarm permissions.",
|
||||
"disableBatteryOptimization": "Disable battery optimization",
|
||||
"batteryOptimizationHelpTitle": "Activer le nettoyage du presse-papier en arrière-plan",
|
||||
"batteryOptimizationActive": "L'optimisation de la batterie bloque les tâches en arrière-plan",
|
||||
"batteryOptimizationDisabled": "Effacement du presse-papiers en arrière-plan activé",
|
||||
"batteryOptimizationHelpDescription": "L'optimisation de la batterie d'Android empêche le nettoyage fiable du presse-papiers lorsque l'application est en arrière-plan. La désactivation de l'optimisation de la batterie pour AliasVault permet un nettoyage précis du presse-papiers et accorde automatiquement les autorisations d'alarme nécessaires.",
|
||||
"disableBatteryOptimization": "Désactiver l'optimisation de la batterie",
|
||||
"identityGenerator": "Générateur d'identité",
|
||||
"passwordGenerator": "Password Generator",
|
||||
"importExport": "Import / Export",
|
||||
"importSectionTitle": "Import",
|
||||
"importSectionDescription": "Import your passwords from other password managers or from a previous AliasVault export.",
|
||||
"importWebNote": "To import credentials from existing password managers, please login to the web app. The import feature is currently only available on the web version.",
|
||||
"exportSectionTitle": "Export",
|
||||
"passwordGenerator": "Générateur de mot de passe",
|
||||
"importExport": "Importer / Exporter",
|
||||
"importSectionTitle": "Importer",
|
||||
"importSectionDescription": "Importez vos mots de passe depuis d'autres gestionnaires de mots de passe ou depuis un précédent export AliasVault.",
|
||||
"importWebNote": "Pour importer des informations d’identification à partir des gestionnaires de mots de passe existants, veuillez vous connecter à l’application Web. La fonction d’importation n’est actuellement disponible que sur la version web.",
|
||||
"exportSectionTitle": "Exporter",
|
||||
"exportSectionDescription": "Export your vault data to a CSV file. This file can be used as a back-up and can also be imported into other password managers.",
|
||||
"exportCsvButton": "Export vault to CSV file",
|
||||
"exporting": "Exporting...",
|
||||
@@ -415,7 +415,7 @@
|
||||
"invalidQrCode": "Invalid QR Code",
|
||||
"notAliasVaultQr": "This is not a valid AliasVault QR code. Please scan a QR code generated by AliasVault.",
|
||||
"cameraPermissionTitle": "Camera Permission Required",
|
||||
"cameraPermissionMessage": "Please allow camera access to scan QR codes.",
|
||||
"cameraPermissionMessage": "Veuillez autoriser l'accès à l'appareil photo pour scanner les QR codes.",
|
||||
"mobileLogin": {
|
||||
"confirmTitle": "Confirm Login Request",
|
||||
"confirmSubtitle": "Re-authenticate to approve login on another device.",
|
||||
@@ -493,7 +493,7 @@
|
||||
"status": {
|
||||
"unlockingVault": "Unlocking vault",
|
||||
"decryptingVault": "Decrypting vault",
|
||||
"openingVaultReadOnly": "Opening vault in read-only mode",
|
||||
"openingVaultReadOnly": "Ouvrir le coffre en mode lecture seule",
|
||||
"retryingConnection": "Retrying connection..."
|
||||
},
|
||||
"offline": {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 70;
|
||||
objectVersion = 60;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
@@ -212,7 +212,7 @@
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
CEE9098F2DA548C7008D568F /* PBXFileSystemSynchronizedBuildFileExceptionSet */ = {
|
||||
CEE9098F2DA548C7008D568F /* Exceptions for "Autofill" folder in "Autofill" target */ = {
|
||||
isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
|
||||
membershipExceptions = (
|
||||
Info.plist,
|
||||
@@ -222,13 +222,84 @@
|
||||
/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||
|
||||
/* Begin PBXFileSystemSynchronizedRootGroup section */
|
||||
CE59C7602E4F47FD0024A246 /* VaultUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultUITests; sourceTree = "<group>"; };
|
||||
CE77825E2EA1822400A75E6F /* VaultUtils */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultUtils; sourceTree = "<group>"; };
|
||||
CEE480882DBE86DC00F4A367 /* VaultStoreKit */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultStoreKit; sourceTree = "<group>"; };
|
||||
CEE480972DBE86DD00F4A367 /* VaultStoreKitTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultStoreKitTests; sourceTree = "<group>"; };
|
||||
CEE4816B2DBE8AC800F4A367 /* VaultUI */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultUI; sourceTree = "<group>"; };
|
||||
CEE482AB2DBE8EFE00F4A367 /* VaultModels */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = VaultModels; sourceTree = "<group>"; };
|
||||
CEE909812DA548C7008D568F /* Autofill */ = {isa = PBXFileSystemSynchronizedRootGroup; exceptions = (CEE9098F2DA548C7008D568F /* PBXFileSystemSynchronizedBuildFileExceptionSet */, ); explicitFileTypes = {}; explicitFolders = (); path = Autofill; sourceTree = "<group>"; };
|
||||
CE59C7602E4F47FD0024A246 /* VaultUITests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CE77825E2EA1822400A75E6F /* VaultUtils */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultUtils;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CEE480882DBE86DC00F4A367 /* VaultStoreKit */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultStoreKit;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CEE480972DBE86DD00F4A367 /* VaultStoreKitTests */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultStoreKitTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CEE4816B2DBE8AC800F4A367 /* VaultUI */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultUI;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CEE482AB2DBE8EFE00F4A367 /* VaultModels */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = VaultModels;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
CEE909812DA548C7008D568F /* Autofill */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
exceptions = (
|
||||
CEE9098F2DA548C7008D568F /* Exceptions for "Autofill" folder in "Autofill" target */,
|
||||
);
|
||||
explicitFileTypes = {
|
||||
};
|
||||
explicitFolders = (
|
||||
);
|
||||
path = Autofill;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -1349,7 +1420,10 @@
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
||||
@@ -1403,7 +1477,10 @@
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
OTHER_LDFLAGS = "$(inherited) ";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
USE_HERMES = true;
|
||||
|
||||
@@ -333,9 +333,9 @@ PODS:
|
||||
- FBLazyVector (0.79.6)
|
||||
- fmt (11.0.2)
|
||||
- glog (0.3.5)
|
||||
- hermes-engine (0.79.5):
|
||||
- hermes-engine/Pre-built (= 0.79.5)
|
||||
- hermes-engine/Pre-built (0.79.5)
|
||||
- hermes-engine (0.79.6):
|
||||
- hermes-engine/Pre-built (= 0.79.6)
|
||||
- hermes-engine/Pre-built (0.79.6)
|
||||
- Macaw (0.9.10):
|
||||
- SWXMLHash
|
||||
- OpenSSL-Universal (3.3.3001)
|
||||
@@ -2442,7 +2442,7 @@ PODS:
|
||||
- SQLite.swift (0.14.1):
|
||||
- SQLite.swift/standard (= 0.14.1)
|
||||
- SQLite.swift/standard (0.14.1)
|
||||
- SwiftLint (0.59.1)
|
||||
- SwiftLint (0.62.2)
|
||||
- SWXMLHash (7.0.2)
|
||||
- Yoga (0.0.0)
|
||||
|
||||
@@ -2795,8 +2795,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost: 1dca942403ed9342f98334bf4c3621f011aa7946
|
||||
DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385
|
||||
boost: 7e761d76ca2ce687f7cc98e698152abd03a18f90
|
||||
DoubleConversion: cb417026b2400c8f53ae97020b2be961b59470cb
|
||||
EXConstants: 98bcf0f22b820f9b28f9fee55ff2daededadd2f8
|
||||
EXJSONUtils: 1d3e4590438c3ee593684186007028a14b3686cd
|
||||
EXManifests: 691a779b04e4f2c96da46fb9bef4f86174fefcb5
|
||||
@@ -2826,9 +2826,9 @@ SPEC CHECKSUMS:
|
||||
EXUpdatesInterface: 7ff005b7af94ee63fa452ea7bb95d7a8ff40277a
|
||||
fast_float: 06eeec4fe712a76acc9376682e4808b05ce978b6
|
||||
FBLazyVector: 07309209b7b914451b8f822544a18e2a0a85afff
|
||||
fmt: 01b82d4ca6470831d1cc0852a1af644be019e8f6
|
||||
glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a
|
||||
hermes-engine: f03b0e06d3882d71e67e45b073bb827da1a21aae
|
||||
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
|
||||
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
|
||||
hermes-engine: 44bb6fe76a6eb400d3a992e2d0b21946ae999fa9
|
||||
Macaw: 7af8ea57aa2cab35b4a52a45e6f85eea753ea9ae
|
||||
OpenSSL-Universal: 6082b0bf950e5636fe0d78def171184e2b3899c2
|
||||
RCT-Folly: e78785aa9ba2ed998ea4151e314036f6c49e6d82
|
||||
@@ -2908,7 +2908,7 @@ SPEC CHECKSUMS:
|
||||
SignalArgon2: 1c24183835ca861e6af06631c18b1671cdf35571
|
||||
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
|
||||
SQLite.swift: 2992550ebf3c5b268bf4352603e3df87d2a4ed72
|
||||
SwiftLint: 3d48e2fb2a3468fdaccf049e5e755df22fb40c2c
|
||||
SwiftLint: f84fc7d844e9cde0dc4f5013af608a269e317aba
|
||||
SWXMLHash: dd733a457e9c4fe93b1538654057aefae4acb382
|
||||
Yoga: dc7c21200195acacb62fa920c588e7c2106de45e
|
||||
|
||||
|
||||
106
apps/mobile-app/package-lock.json
generated
106
apps/mobile-app/package-lock.json
generated
@@ -6416,24 +6416,24 @@
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.3",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
|
||||
"integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
|
||||
"version": "1.20.4",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
|
||||
"integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"bytes": "~3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.13.0",
|
||||
"raw-body": "2.5.2",
|
||||
"destroy": "~1.2.0",
|
||||
"http-errors": "~2.0.1",
|
||||
"iconv-lite": "~0.4.24",
|
||||
"on-finished": "~2.4.1",
|
||||
"qs": "~6.14.0",
|
||||
"raw-body": "~2.5.3",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
@@ -6450,6 +6450,27 @@
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/http-errors": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
||||
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "~2.0.0",
|
||||
"inherits": "~2.0.4",
|
||||
"setprototypeof": "~1.2.0",
|
||||
"statuses": "~2.0.2",
|
||||
"toidentifier": "~1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/body-parser/node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
@@ -6457,6 +6478,16 @@
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/body-parser/node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/boolbase": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
@@ -14354,13 +14385,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.13.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
|
||||
"integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
|
||||
"version": "6.14.1",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
|
||||
"integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
|
||||
"devOptional": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.6"
|
||||
"side-channel": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
@@ -14444,21 +14475,52 @@
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"version": "2.5.3",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
|
||||
"integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
"bytes": "~3.1.2",
|
||||
"http-errors": "~2.0.1",
|
||||
"iconv-lite": "~0.4.24",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/http-errors": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
|
||||
"integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"depd": "~2.0.0",
|
||||
"inherits": "~2.0.4",
|
||||
"setprototypeof": "~1.2.0",
|
||||
"statuses": "~2.0.2",
|
||||
"toidentifier": "~1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body/node_modules/statuses": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
|
||||
"integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/rc": {
|
||||
"version": "1.2.8",
|
||||
"resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"loading": {
|
||||
"title": "AliasVault wordt geladen",
|
||||
"message": "De beveiligde omgeving wordt opgestart. AliasVault draait volledig in je browser, waardoor het laden de eerste keer iets langer kan duren.",
|
||||
"message": "De beveiligde omgeving wordt gestart. AliasVault draait volledig in je browser, waardoor het laden de eerste keer iets langer kan duren.",
|
||||
"refreshText": "Als het laden vastloopt, kunt je op de onderstaande knop klikken om de pagina te vernieuwen.",
|
||||
"refreshButtonText": "Vernieuwen"
|
||||
},
|
||||
|
||||
@@ -275,7 +275,7 @@ GEM
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
unicode-display_width (1.8.0)
|
||||
uri (1.0.3)
|
||||
uri (1.0.4)
|
||||
webrick (1.9.0)
|
||||
|
||||
PLATFORMS
|
||||
|
||||
61
docs/misc/dev/release/git-versioning-strategy.md
Normal file
61
docs/misc/dev/release/git-versioning-strategy.md
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
layout: default
|
||||
title: Git versioning strategy
|
||||
parent: Release
|
||||
grand_parent: Development
|
||||
nav_order: 3
|
||||
---
|
||||
|
||||
# Git versioning strategy
|
||||
|
||||
This document describes the **official release workflow** for AliasVault.
|
||||
|
||||
## Branch Semantics
|
||||
|
||||
### `main`
|
||||
- Represents the **next version line**
|
||||
- Contains **only pre-release versions**
|
||||
- Example versions:
|
||||
- `0.26.0-alpha`
|
||||
- `0.26.0-beta`
|
||||
- Never tagged for stable releases
|
||||
|
||||
---
|
||||
|
||||
### `XXXX-*` (GitHub issue branches)
|
||||
- Branch from:
|
||||
- `main` for next-version development, or
|
||||
- a release tag for hotfixes
|
||||
- Contains **only code fixes**
|
||||
- No version changes
|
||||
- No release notes
|
||||
- May contain many commits
|
||||
|
||||
Landing rules:
|
||||
- If branched from `main`: merge or rebase back into `main`
|
||||
- If branched from a tag: **cherry-pick fixes into `main`**
|
||||
- May be merged into a `release/*` branch for packaging
|
||||
|
||||
---
|
||||
|
||||
### `release/*`
|
||||
- Used only to **package a stable release**
|
||||
- Contains:
|
||||
- fixes (cherry-picked back into main)
|
||||
- release notes (cherry-picked back into main)
|
||||
- version bump
|
||||
- Never merged into `main`
|
||||
- Deleted after tagging
|
||||
|
||||
---
|
||||
|
||||
## Versioning Rules
|
||||
|
||||
### Development versions
|
||||
- Live only on `main`
|
||||
- Always pre-release (`-alpha`, `-beta`, etc.)
|
||||
|
||||
### Stable versions
|
||||
- Live only on `release/*` branches
|
||||
- Always tagged
|
||||
- Never merged back into `main`
|
||||
65
docs/misc/dev/release/release-checklist.md
Normal file
65
docs/misc/dev/release/release-checklist.md
Normal file
@@ -0,0 +1,65 @@
|
||||
---
|
||||
layout: default
|
||||
title: Release Checklist
|
||||
parent: Release
|
||||
grand_parent: Development
|
||||
nav_order: 4
|
||||
---
|
||||
|
||||
# Release Checklist
|
||||
|
||||
Step-by-step guide for creating a new AliasVault release.
|
||||
|
||||
## 1. Create release branch
|
||||
|
||||
- **Feature release**: Branch from `main`
|
||||
|
||||
```bash
|
||||
# Feature release
|
||||
git checkout main && git checkout -b release/X.Y.Z
|
||||
```
|
||||
|
||||
- **Bug/hotfix release**: Branch from previous tag (e.g., `0.25.2`)
|
||||
|
||||
```bash
|
||||
# Hotfix release
|
||||
git checkout 0.25.2 && git checkout -b release/0.25.3
|
||||
```
|
||||
|
||||
## 2. Bump version and write release notes
|
||||
|
||||
Run the version bump script which automatically bumps all versions and creates Fastlane changelog files:
|
||||
|
||||
```bash
|
||||
./scripts/bump-version.sh
|
||||
```
|
||||
|
||||
- Commit the release notes in its own commit first
|
||||
- **Cherry-pick the release notes commit to `main`**
|
||||
- Commit the version bump changes in a separate commit
|
||||
- The version bump commit stays only in the release branch
|
||||
- ***Not cherry-picked***, as the `main` branch is always targeting the next feature (pre)release
|
||||
|
||||
## 3. Additional changes (optional)
|
||||
|
||||
- If additional fixes are needed after testing, add them to the release branch
|
||||
- **Cherry-pick all fix commits back to `main`**
|
||||
|
||||
## 4. Publish release
|
||||
|
||||
- Create the release from GitHub Releases based on the release branch
|
||||
- Tag is created automatically
|
||||
|
||||
## 5. Verify cherry-picks
|
||||
|
||||
After release, verify all relevant changes were cherry-picked to `main`:
|
||||
|
||||
```bash
|
||||
git range-diff <previous-tag>..release/<version> <previous-tag>..main
|
||||
# Example:
|
||||
git range-diff 0.25.2..release/0.25.3 0.25.2..main
|
||||
```
|
||||
|
||||
**Expected output:**
|
||||
- Only the version bump commit should show as `<` (only in release branch)
|
||||
- All other commits should show as `=` (in both branches)
|
||||
2
fastlane/metadata/android/en-US/changelogs/2503901.txt
Normal file
2
fastlane/metadata/android/en-US/changelogs/2503901.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
- Android Passkey compatibility improvements
|
||||
- Add French and Spanish language options
|
||||
2
fastlane/metadata/android/nl-NL/changelogs/2503901.txt
Normal file
2
fastlane/metadata/android/nl-NL/changelogs/2503901.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
- Android Passkey compatibiliteit verbeterd
|
||||
- Frans en Spaans toegevoegd als taalopties
|
||||
@@ -0,0 +1 @@
|
||||
- Add French and Spanish language options
|
||||
1
fastlane/metadata/ios/en-US/changelogs/2503900.txt
Normal file
1
fastlane/metadata/ios/en-US/changelogs/2503900.txt
Normal file
@@ -0,0 +1 @@
|
||||
- Add French and Spanish language options
|
||||
1
fastlane/metadata/ios/nl-NL/changelogs/2503900.txt
Normal file
1
fastlane/metadata/ios/nl-NL/changelogs/2503900.txt
Normal file
@@ -0,0 +1 @@
|
||||
- Frans en Spaans toegevoegd als taalopties
|
||||
80
scripts/print-latest-changelogs.sh
Executable file
80
scripts/print-latest-changelogs.sh
Executable file
@@ -0,0 +1,80 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Show the latest changelogs for all platforms (Android, iOS, Browser Extension)
|
||||
# Outputs formatted changelog content for easy copy-paste
|
||||
#
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||
METADATA_DIR="$ROOT_DIR/fastlane/metadata"
|
||||
|
||||
# Function to get the latest changelog file from a directory
|
||||
get_latest_changelog() {
|
||||
local dir="$1"
|
||||
if [ -d "$dir" ]; then
|
||||
# Sort files by version number (handles both numeric and semver formats)
|
||||
ls -1 "$dir" 2>/dev/null | sort -V | tail -1
|
||||
fi
|
||||
}
|
||||
|
||||
# Function to print Android changelogs (XML format, no spaces between languages)
|
||||
print_android() {
|
||||
local changelog_dir="$METADATA_DIR/android/en-US/changelogs"
|
||||
local latest_file=$(get_latest_changelog "$changelog_dir")
|
||||
|
||||
echo "================================================================================"
|
||||
echo "ANDROID (latest: $latest_file)"
|
||||
echo "================================================================================"
|
||||
echo ""
|
||||
|
||||
# Print all locales in XML format without blank lines between them
|
||||
for locale_dir in "$METADATA_DIR/android"/*; do
|
||||
if [ -d "$locale_dir/changelogs" ]; then
|
||||
locale=$(basename "$locale_dir")
|
||||
local file="$locale_dir/changelogs/$latest_file"
|
||||
if [ -f "$file" ]; then
|
||||
echo "<$locale>"
|
||||
cat "$file"
|
||||
echo "</$locale>"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Function to print iOS/Browser Extension changelogs (each language separate)
|
||||
print_simple() {
|
||||
local platform="$1"
|
||||
local display_name="$2"
|
||||
local changelog_dir="$METADATA_DIR/$platform/en-US/changelogs"
|
||||
local latest_file=$(get_latest_changelog "$changelog_dir")
|
||||
|
||||
echo "================================================================================"
|
||||
echo "$display_name (latest: $latest_file)"
|
||||
echo "================================================================================"
|
||||
|
||||
# Print each locale separately
|
||||
for locale_dir in "$METADATA_DIR/$platform"/*; do
|
||||
if [ -d "$locale_dir/changelogs" ]; then
|
||||
locale=$(basename "$locale_dir")
|
||||
local file="$locale_dir/changelogs/$latest_file"
|
||||
if [ -f "$file" ]; then
|
||||
echo ""
|
||||
echo "--- $locale ---"
|
||||
cat "$file"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
echo ""
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "Latest Changelogs Summary"
|
||||
echo ""
|
||||
|
||||
print_android
|
||||
print_simple "ios" "iOS"
|
||||
print_simple "browser-extension" "BROWSER EXTENSION"
|
||||
|
||||
echo "================================================================================"
|
||||
echo ""
|
||||
Reference in New Issue
Block a user