Migrate to SecureStorage
This commit is contained in:
@@ -116,4 +116,6 @@ dependencies {
|
|||||||
implementation 'androidx.activity:activity-ktx:1.13.0'
|
implementation 'androidx.activity:activity-ktx:1.13.0'
|
||||||
implementation 'androidx.security:security-crypto:1.1.0'
|
implementation 'androidx.security:security-crypto:1.1.0'
|
||||||
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.11.0'
|
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.11.0'
|
||||||
|
implementation "androidx.datastore:datastore-preferences:1.2.1"
|
||||||
|
implementation "com.google.crypto.tink:tink-android:1.22.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ class AccountActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private lateinit var preferences: SharedPreferences
|
private lateinit var preferences: SharedPreferences
|
||||||
private var oldPassword = ""
|
|
||||||
private var useWebLogin = true
|
private var useWebLogin = true
|
||||||
private var showLoginDialog by mutableStateOf(false)
|
private var showLoginDialog by mutableStateOf(false)
|
||||||
|
|
||||||
@@ -222,11 +221,7 @@ class AccountActivity : AppCompatActivity() {
|
|||||||
private fun legacyLogin() {
|
private fun legacyLogin() {
|
||||||
val url = CospendClientUtil.formatURL(viewModel.serverUrl.trim())
|
val url = CospendClientUtil.formatURL(viewModel.serverUrl.trim())
|
||||||
val username = viewModel.username
|
val username = viewModel.username
|
||||||
var password = viewModel.password
|
val password = viewModel.password
|
||||||
|
|
||||||
if (password.isEmpty()) {
|
|
||||||
password = oldPassword
|
|
||||||
}
|
|
||||||
|
|
||||||
performLogin(url, username, password)
|
performLogin(url, username, password)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,14 +61,15 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
|
|||||||
} else {
|
} else {
|
||||||
preferences.getString(AccountActivity.SETTINGS_USERNAME, "")
|
preferences.getString(AccountActivity.SETTINGS_USERNAME, "")
|
||||||
}
|
}
|
||||||
val password = if (useSso) {
|
|
||||||
""
|
|
||||||
} else {
|
|
||||||
SecureStorage.getPassword(getApplication(), AccountActivity.SETTINGS_PASSWORD)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!url.isNullOrEmpty() && !username.isNullOrEmpty()) {
|
if (!url.isNullOrEmpty() && !username.isNullOrEmpty()) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
val password = if (useSso) {
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
SecureStorage.getPassword(getApplication(), AccountActivity.SETTINGS_PASSWORD)
|
||||||
|
}
|
||||||
|
|
||||||
isValidatingLogin = true
|
isValidatingLogin = true
|
||||||
isLoggedIn = withContext(Dispatchers.IO) {
|
isLoggedIn = withContext(Dispatchers.IO) {
|
||||||
if (useSso) {
|
if (useSso) {
|
||||||
@@ -88,7 +89,9 @@ class AccountViewModel(application: Application) : AndroidViewModel(application)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun logout() {
|
fun logout() {
|
||||||
SecureStorage.removePassword(getApplication(), AccountActivity.SETTINGS_PASSWORD)
|
viewModelScope.launch {
|
||||||
|
SecureStorage.removePassword(getApplication(), AccountActivity.SETTINGS_PASSWORD)
|
||||||
|
}
|
||||||
preferences.edit {
|
preferences.edit {
|
||||||
remove(AccountActivity.SETTINGS_USE_SSO)
|
remove(AccountActivity.SETTINGS_USE_SSO)
|
||||||
remove(AccountActivity.SETTINGS_SSO_URL)
|
remove(AccountActivity.SETTINGS_SSO_URL)
|
||||||
|
|||||||
@@ -121,13 +121,7 @@ fun NewProjectScreen(
|
|||||||
Spacer(modifier = Modifier.height(8.dp))
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
if (viewModel.whatTodoIsCreate) {
|
if (viewModel.whatTodoIsCreate) {
|
||||||
if (viewModel.projectType == ProjectType.COSPEND) {
|
if (viewModel.projectType == ProjectType.LOCAL) {
|
||||||
Button(
|
|
||||||
onClick = onChooseFromNextcloud
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.new_project_from_nextcloud_tooltip))
|
|
||||||
}
|
|
||||||
} else if (viewModel.projectType == ProjectType.LOCAL) {
|
|
||||||
Button(
|
Button(
|
||||||
onClick = onImportFile
|
onClick = onImportFile
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import java.io.Serializable
|
|||||||
class DBProject(
|
class DBProject(
|
||||||
var id: Long,
|
var id: Long,
|
||||||
var remoteId: String,
|
var remoteId: String,
|
||||||
var password: String?,
|
var password: String,
|
||||||
var name: String,
|
var name: String,
|
||||||
var serverUrl: String?,
|
var serverUrl: String?,
|
||||||
var email: String?,
|
var email: String?,
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
values.put(key_name, accountProject.name)
|
values.put(key_name, accountProject.name)
|
||||||
values.put(key_archived, accountProject.archivedTs ?: 0L)
|
values.put(key_archived, accountProject.archivedTs ?: 0L)
|
||||||
val id = db.insert(table_account_projects, null, values)
|
val id = db.insert(table_account_projects, null, values)
|
||||||
SecureStorage.savePassword(context, "AccountProjectPassword_$id", accountProject.password)
|
SecureStorage.savePasswordSync(context, "AccountProjectPassword_$id", accountProject.password)
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -348,7 +348,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
private fun getAccountProjectFromCursor(cursor: Cursor): DBAccountProject {
|
private fun getAccountProjectFromCursor(cursor: Cursor): DBAccountProject {
|
||||||
val id = cursor.getLong(cursor.getColumnIndex(key_id))
|
val id = cursor.getLong(cursor.getColumnIndex(key_id))
|
||||||
val archivedTs = cursor.getLong(cursor.getColumnIndex(key_archived))
|
val archivedTs = cursor.getLong(cursor.getColumnIndex(key_archived))
|
||||||
val password = SecureStorage.getPassword(context, "AccountProjectPassword_$id")
|
val password = SecureStorage.getPasswordSync(context, "AccountProjectPassword_$id")
|
||||||
return DBAccountProject(
|
return DBAccountProject(
|
||||||
id,
|
id,
|
||||||
cursor.getString(cursor.getColumnIndex(key_remoteId)),
|
cursor.getString(cursor.getColumnIndex(key_remoteId)),
|
||||||
@@ -619,7 +619,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
values.put(key_type, project.type.id)
|
values.put(key_type, project.type.id)
|
||||||
values.put(key_archived, project.archivedTs ?: 0L)
|
values.put(key_archived, project.archivedTs ?: 0L)
|
||||||
val id = db.insert(table_projects, null, values)
|
val id = db.insert(table_projects, null, values)
|
||||||
SecureStorage.savePassword(context, "ProjectPassword_$id", project.password)
|
SecureStorage.savePasswordSync(context, "ProjectPassword_$id", project.password)
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -651,7 +651,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
private fun getProjectFromCursor(cursor: Cursor): DBProject {
|
private fun getProjectFromCursor(cursor: Cursor): DBProject {
|
||||||
val id = cursor.getLong(cursor.getColumnIndex(key_id))
|
val id = cursor.getLong(cursor.getColumnIndex(key_id))
|
||||||
val archivedTs = cursor.getLong(cursor.getColumnIndex(key_archived))
|
val archivedTs = cursor.getLong(cursor.getColumnIndex(key_archived))
|
||||||
val password = SecureStorage.getPassword(context, "ProjectPassword_$id")
|
val password = SecureStorage.getPasswordSync(context, "ProjectPassword_$id") ?: ""
|
||||||
return DBProject(
|
return DBProject(
|
||||||
id,
|
id,
|
||||||
cursor.getString(cursor.getColumnIndex(key_remoteId)),
|
cursor.getString(cursor.getColumnIndex(key_remoteId)),
|
||||||
@@ -678,7 +678,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
}
|
}
|
||||||
db.delete(table_members, "$key_projectid = ?", arrayOf(id.toString()))
|
db.delete(table_members, "$key_projectid = ?", arrayOf(id.toString()))
|
||||||
db.delete(table_projects, "$key_id = ?", arrayOf(id.toString()))
|
db.delete(table_projects, "$key_id = ?", arrayOf(id.toString()))
|
||||||
SecureStorage.removePassword(context, "ProjectPassword_$id")
|
SecureStorage.removePasswordSync(context, "ProjectPassword_$id")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateProject(
|
fun updateProject(
|
||||||
@@ -693,7 +693,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
val values = ContentValues()
|
val values = ContentValues()
|
||||||
if (newName != null) values.put(key_name, newName)
|
if (newName != null) values.put(key_name, newName)
|
||||||
if (newEmail != null) values.put(key_email, newEmail)
|
if (newEmail != null) values.put(key_email, newEmail)
|
||||||
if (newPassword != null) SecureStorage.savePassword(context, "ProjectPassword_$projId", newPassword)
|
if (newPassword != null) SecureStorage.savePasswordSync(context, "ProjectPassword_$projId", newPassword)
|
||||||
if (newBearerToken != null) values.put(key_bearer_token, newBearerToken)
|
if (newBearerToken != null) values.put(key_bearer_token, newBearerToken)
|
||||||
if (newLastPayerId != null) values.put(key_lastPayerId, newLastPayerId)
|
if (newLastPayerId != null) values.put(key_lastPayerId, newLastPayerId)
|
||||||
if (newLastSyncedTimestamp != null) values.put(key_lastSyncTimestamp, newLastSyncedTimestamp)
|
if (newLastSyncedTimestamp != null) values.put(key_lastSyncTimestamp, newLastSyncedTimestamp)
|
||||||
@@ -733,7 +733,7 @@ class CowspentSQLiteOpenHelper private constructor(val context: Context) :
|
|||||||
val values = ContentValues()
|
val values = ContentValues()
|
||||||
if (newName != null) values.put(key_name, newName)
|
if (newName != null) values.put(key_name, newName)
|
||||||
if (newEmail != null) values.put(key_email, newEmail)
|
if (newEmail != null) values.put(key_email, newEmail)
|
||||||
if (newPassword != null) SecureStorage.savePassword(context, "ProjectPassword_$projId", newPassword)
|
if (newPassword != null) SecureStorage.savePasswordSync(context, "ProjectPassword_$projId", newPassword)
|
||||||
if (newBearerToken != null) values.put(key_bearer_token, newBearerToken)
|
if (newBearerToken != null) values.put(key_bearer_token, newBearerToken)
|
||||||
if (newLastPayerId != null) values.put(key_lastPayerId, newLastPayerId)
|
if (newLastPayerId != null) values.put(key_lastPayerId, newLastPayerId)
|
||||||
if (newLastSyncedTimestamp != null) values.put(key_lastSyncTimestamp, newLastSyncedTimestamp)
|
if (newLastSyncedTimestamp != null) values.put(key_lastSyncTimestamp, newLastSyncedTimestamp)
|
||||||
|
|||||||
@@ -1,47 +1,84 @@
|
|||||||
package net.helcel.cowspent.util
|
package net.helcel.cowspent.util
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.util.Base64
|
||||||
import androidx.security.crypto.EncryptedSharedPreferences
|
import androidx.datastore.core.DataStore
|
||||||
import androidx.security.crypto.MasterKey
|
import androidx.datastore.preferences.core.Preferences
|
||||||
import androidx.core.content.edit
|
import androidx.datastore.preferences.core.edit
|
||||||
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
|
import com.google.crypto.tink.Aead
|
||||||
|
import com.google.crypto.tink.KeyTemplates
|
||||||
|
import com.google.crypto.tink.RegistryConfiguration
|
||||||
|
import com.google.crypto.tink.aead.AeadConfig
|
||||||
|
import com.google.crypto.tink.integration.android.AndroidKeysetManager
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
|
||||||
|
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "secure_prefs")
|
||||||
|
|
||||||
object SecureStorage {
|
object SecureStorage {
|
||||||
private const val SECURE_PREFS_NAME = "secure_prefs"
|
private var aead: Aead? = null
|
||||||
private var encryptedPrefs: SharedPreferences? = null
|
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun getEncryptedPrefs(context: Context): SharedPreferences {
|
private fun getAead(context: Context): Aead {
|
||||||
encryptedPrefs?.let { return it }
|
aead?.let { return it }
|
||||||
|
AeadConfig.register()
|
||||||
val masterKey = MasterKey.Builder(context)
|
val masterKeyUri = "android-keystore://secure_storage_master_key"
|
||||||
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
val keysetManager = AndroidKeysetManager.Builder()
|
||||||
|
.withSharedPref(context, "tink_keyset", "secure_storage_keyset_prefs")
|
||||||
|
.withKeyTemplate(KeyTemplates.get("AES256_GCM"))
|
||||||
|
.withMasterKeyUri(masterKeyUri)
|
||||||
.build()
|
.build()
|
||||||
|
val newAead = keysetManager.keysetHandle.getPrimitive(RegistryConfiguration.get(), Aead::class.java)
|
||||||
val prefs = EncryptedSharedPreferences.create(
|
aead = newAead
|
||||||
context,
|
return newAead
|
||||||
SECURE_PREFS_NAME,
|
|
||||||
masterKey,
|
|
||||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
|
||||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
|
||||||
)
|
|
||||||
encryptedPrefs = prefs
|
|
||||||
return prefs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun savePassword(context: Context, key: String, password: String?) {
|
private fun encrypt(context: Context, value: String): String {
|
||||||
if (password == null) {
|
val encrypted = getAead(context).encrypt(value.toByteArray(), null)
|
||||||
removePassword(context, key)
|
return Base64.encodeToString(encrypted, Base64.NO_WRAP)
|
||||||
} else {
|
}
|
||||||
getEncryptedPrefs(context).edit { putString(key, password) }
|
|
||||||
|
private fun decrypt(context: Context, encryptedValue: String): String? {
|
||||||
|
return try {
|
||||||
|
val decoded = Base64.decode(encryptedValue, Base64.NO_WRAP)
|
||||||
|
val decrypted = getAead(context).decrypt(decoded, null)
|
||||||
|
String(decrypted)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getPassword(context: Context, key: String): String? {
|
suspend fun savePassword(context: Context, key: String, password: String?) {
|
||||||
return getEncryptedPrefs(context).getString(key, null)
|
if (password == null) {
|
||||||
|
removePassword(context, key)
|
||||||
|
} else {
|
||||||
|
val encrypted = encrypt(context, password)
|
||||||
|
context.dataStore.edit { it[stringPreferencesKey(key)] = encrypted }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removePassword(context: Context, key: String) {
|
suspend fun getPassword(context: Context, key: String): String? {
|
||||||
getEncryptedPrefs(context).edit { remove(key) }
|
val encrypted = context.dataStore.data.map { it[stringPreferencesKey(key)] }.first()
|
||||||
|
return encrypted?.let { decrypt(context, it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun removePassword(context: Context, key: String) {
|
||||||
|
context.dataStore.edit { it.remove(stringPreferencesKey(key)) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Synchronous alternatives for legacy code
|
||||||
|
fun savePasswordSync(context: Context, key: String, password: String?) = runBlocking {
|
||||||
|
savePassword(context, key, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPasswordSync(context: Context, key: String): String? = runBlocking {
|
||||||
|
getPassword(context, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removePasswordSync(context: Context, key: String) = runBlocking {
|
||||||
|
removePassword(context, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -333,7 +333,6 @@ class VersatileProjectSyncClient(
|
|||||||
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills/" + bill.remoteId
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills/" + bill.remoteId
|
||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills/" + bill.remoteId
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills/" + bill.remoteId
|
||||||
Log.i(TAG, "using public API, target is: ${SupportUtil.maskUrl(target)} for editRemoteBill")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/bills/" + bill.remoteId
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/bills/" + bill.remoteId
|
||||||
@@ -386,7 +385,6 @@ class VersatileProjectSyncClient(
|
|||||||
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password)
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password)
|
||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password)
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password)
|
||||||
Log.i(TAG, "using public API, target is: ${SupportUtil.maskUrl(target)} for deleteRemoteProject")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId
|
||||||
@@ -433,7 +431,6 @@ class VersatileProjectSyncClient(
|
|||||||
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills/" + billRemoteId
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills/" + billRemoteId
|
||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills/" + billRemoteId
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills/" + billRemoteId
|
||||||
Log.i(TAG, "using public API, target is: ${SupportUtil.maskUrl(target)} for deleteRemoteProject")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/bills/" + billRemoteId
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/bills/" + billRemoteId
|
||||||
@@ -504,8 +501,7 @@ class VersatileProjectSyncClient(
|
|||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/api-priv/projects"
|
project.getRequestBaseUrl(false) + "/api-priv/projects"
|
||||||
useOcsApiRequest = cospendVersionGT161
|
useOcsApiRequest = cospendVersionGT161
|
||||||
if (cospendVersionGT161) {
|
|
||||||
}
|
|
||||||
return ServerResponse.CreateRemoteProjectResponse(
|
return ServerResponse.CreateRemoteProjectResponse(
|
||||||
requestServer(
|
requestServer(
|
||||||
target, METHOD_POST, paramKeys, paramValues,
|
target, METHOD_POST, paramKeys, paramValues,
|
||||||
@@ -584,7 +580,6 @@ class VersatileProjectSyncClient(
|
|||||||
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills"
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills"
|
||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills"
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills"
|
||||||
Log.i(TAG, "using public API, target is: ${SupportUtil.maskUrl(target)} for createRemoteBill")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/bills"
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/bills"
|
||||||
@@ -651,7 +646,6 @@ class VersatileProjectSyncClient(
|
|||||||
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/members"
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/members"
|
||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/members"
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/members"
|
||||||
Log.i(TAG, "using public API, target is: ${SupportUtil.maskUrl(target)} for createRemoteBill")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/members"
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/members"
|
||||||
@@ -686,8 +680,6 @@ class VersatileProjectSyncClient(
|
|||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId + "/bills?lastchanged=" + tsLastSync
|
project.getRequestBaseUrl(false) + "/api-priv/projects/" + project.remoteId + "/bills?lastchanged=" + tsLastSync
|
||||||
useOcsApiRequest = cospendVersionGT161
|
useOcsApiRequest = cospendVersionGT161
|
||||||
if (cospendVersionGT161) {
|
|
||||||
}
|
|
||||||
return ServerResponse.BillsResponse(
|
return ServerResponse.BillsResponse(
|
||||||
requestServer(
|
requestServer(
|
||||||
target, METHOD_GET, null, null,
|
target, METHOD_GET, null, null,
|
||||||
@@ -718,7 +710,6 @@ class VersatileProjectSyncClient(
|
|||||||
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills?lastChanged=" + tsLastSync
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills?lastChanged=" + tsLastSync
|
||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/apiv2/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills?lastchanged=" + tsLastSync
|
project.getRequestBaseUrl(false) + "/apiv2/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/bills?lastchanged=" + tsLastSync
|
||||||
Log.i(TAG, "using public API, target is: ${SupportUtil.maskUrl(target)} for getBills")
|
|
||||||
return ServerResponse.BillsResponse(
|
return ServerResponse.BillsResponse(
|
||||||
requestServer(
|
requestServer(
|
||||||
target, METHOD_GET, null, null,
|
target, METHOD_GET, null, null,
|
||||||
@@ -773,7 +764,6 @@ class VersatileProjectSyncClient(
|
|||||||
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/members"
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/members"
|
||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/members"
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/members"
|
||||||
Log.i(TAG, "using public API, target is: ${SupportUtil.maskUrl(target)} for getMembers")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/members"
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/members"
|
||||||
@@ -827,7 +817,6 @@ class VersatileProjectSyncClient(
|
|||||||
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency"
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency"
|
||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency"
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency"
|
||||||
Log.i(TAG, "using public API, target is: ${SupportUtil.maskUrl(target)} for createRemoteCurrency")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/currency"
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/currency"
|
||||||
@@ -881,7 +870,6 @@ class VersatileProjectSyncClient(
|
|||||||
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency/" + currency.remoteId
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency/" + currency.remoteId
|
||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency/" + currency.remoteId
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency/" + currency.remoteId
|
||||||
Log.i(TAG, "using public API, target is: ${SupportUtil.maskUrl(target)} for createRemoteCurrency")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/currency/" + currency.remoteId
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/currency/" + currency.remoteId
|
||||||
@@ -928,7 +916,6 @@ class VersatileProjectSyncClient(
|
|||||||
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency/" + currencyRemoteId
|
project.getRequestBaseUrl(true) + "/api/v1/public/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency/" + currencyRemoteId
|
||||||
else
|
else
|
||||||
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency/" + currencyRemoteId
|
project.getRequestBaseUrl(false) + "/api/projects/" + project.remoteId + "/" + getEncodedPassword(project.password) + "/currency/" + currencyRemoteId
|
||||||
Log.i(TAG, "using public API, target is: ${SupportUtil.maskUrl(target)} for deleteRemoteCurrency")
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/currency/" + currencyRemoteId
|
target = project.serverUrl!!.replace("/+$".toRegex(), "") + "/api/projects/" + project.remoteId + "/currency/" + currencyRemoteId
|
||||||
@@ -1027,7 +1014,6 @@ class VersatileProjectSyncClient(
|
|||||||
httpCon.setRequestProperty("Accept", "application/json")
|
httpCon.setRequestProperty("Accept", "application/json")
|
||||||
}
|
}
|
||||||
httpCon.connectTimeout = 10 * 1000 // 10 seconds
|
httpCon.connectTimeout = 10 * 1000 // 10 seconds
|
||||||
Log.d(javaClass.simpleName, "$method ${SupportUtil.maskUrl(target)}")
|
|
||||||
if (paramKeys != null && paramValues != null) {
|
if (paramKeys != null && paramValues != null) {
|
||||||
var dataString = ""
|
var dataString = ""
|
||||||
for (i in paramKeys.indices) {
|
for (i in paramKeys.indices) {
|
||||||
@@ -1040,7 +1026,6 @@ class VersatileProjectSyncClient(
|
|||||||
dataString += URLEncoder.encode(value, "UTF-8")
|
dataString += URLEncoder.encode(value, "UTF-8")
|
||||||
}
|
}
|
||||||
val data = dataString.toByteArray()
|
val data = dataString.toByteArray()
|
||||||
Log.d(javaClass.simpleName, "Params: ${SupportUtil.maskParams(dataString)}")
|
|
||||||
httpCon.setFixedLengthStreamingMode(data.size)
|
httpCon.setFixedLengthStreamingMode(data.size)
|
||||||
httpCon.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
|
httpCon.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
|
||||||
httpCon.setRequestProperty("Content-Length", data.size.toString())
|
httpCon.setRequestProperty("Content-Length", data.size.toString())
|
||||||
|
|||||||
Reference in New Issue
Block a user