Start working on audit logs

This commit is contained in:
Michael Schättgen 2024-03-31 22:32:56 +02:00
parent 3c124deae1
commit 171da34b13
31 changed files with 834 additions and 7 deletions

View file

@ -17,14 +17,23 @@ import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ProcessLifecycleOwner;
import androidx.room.Room;
import com.beemdevelopment.aegis.database.AppDatabase;
import com.beemdevelopment.aegis.database.AuditLogEntry;
import com.beemdevelopment.aegis.database.AuditLogRepository;
import com.beemdevelopment.aegis.receivers.VaultLockReceiver;
import com.beemdevelopment.aegis.ui.MainActivity;
import com.beemdevelopment.aegis.util.IOUtils;
import com.beemdevelopment.aegis.vault.VaultManager;
import com.topjohnwu.superuser.Shell;
import org.checkerframework.checker.units.qual.A;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import dagger.hilt.InstallIn;
import dagger.hilt.android.EarlyEntryPoint;

View file

@ -8,6 +8,8 @@ import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import com.beemdevelopment.aegis.database.AppDatabase;
import com.beemdevelopment.aegis.database.AuditLogRepository;
import com.beemdevelopment.aegis.util.IOUtils;
import com.beemdevelopment.aegis.vault.VaultFile;
import com.beemdevelopment.aegis.vault.VaultRepository;
@ -25,12 +27,16 @@ public class AegisBackupAgent extends BackupAgent {
private Preferences _prefs;
private AuditLogRepository _auditLogRepository;
@Override
public void onCreate() {
super.onCreate();
// Cannot use injection with Dagger Hilt here, because the app is launched in a restricted mode on restore
_prefs = new Preferences(this);
AppDatabase appDatabase = AegisModule.provideAppDatabase(this);
_auditLogRepository = AegisModule.provideAuditLogRepository(appDatabase);
}
@Override
@ -53,6 +59,7 @@ public class AegisBackupAgent extends BackupAgent {
// report any runtime exceptions, in addition to the expected IOExceptions.
try {
fullBackup(data);
_auditLogRepository.addAndroidBackupCreatedEvent();
_prefs.setAndroidBackupResult(new Preferences.BackupResult(null));
} catch (Exception e) {
Log.e(TAG, String.format("onFullBackup() failed: %s", e));

View file

@ -2,6 +2,11 @@ package com.beemdevelopment.aegis;
import android.content.Context;
import androidx.room.Room;
import com.beemdevelopment.aegis.database.AppDatabase;
import com.beemdevelopment.aegis.database.AuditLogDao;
import com.beemdevelopment.aegis.database.AuditLogRepository;
import com.beemdevelopment.aegis.icons.IconPackManager;
import com.beemdevelopment.aegis.vault.VaultManager;
@ -24,12 +29,27 @@ public class AegisModule {
@Provides
@Singleton
public static VaultManager provideVaultManager(@ApplicationContext Context context) {
return new VaultManager(context);
public static AuditLogRepository provideAuditLogRepository(AppDatabase appDatabase) {
AuditLogDao auditLogDao = appDatabase.auditLogDao();
return new AuditLogRepository(auditLogDao);
}
@Provides
@Singleton
public static VaultManager provideVaultManager(@ApplicationContext Context context, AuditLogRepository auditLogRepository) {
return new VaultManager(context, auditLogRepository);
}
@Provides
public static Preferences providePreferences(@ApplicationContext Context context) {
return new Preferences(context);
}
@Provides
@Singleton
public static AppDatabase provideAppDatabase(@ApplicationContext Context context) {
return Room.databaseBuilder(context.getApplicationContext(),
AppDatabase.class, "aegis-db")
.build();
}
}

View file

@ -0,0 +1,42 @@
package com.beemdevelopment.aegis;
public enum EventType {
VAULT_UNLOCKED,
VAULT_BACKUP_CREATED,
VAULT_ANDROID_BACKUP_CREATED,
VAULT_EXPORTED,
ENTRY_SHARED,
VAULT_UNLOCK_FAILED_PASSWORD,
VAULT_UNLOCK_FAILED_BIOMETRICS;
private static EventType[] _values;
static {
_values = values();
}
public static EventType fromInteger(int x) {
return _values[x];
}
public static int getEventTitleRes(EventType eventType) {
switch (eventType) {
case VAULT_UNLOCKED:
return R.string.event_title_vault_unlocked;
case VAULT_BACKUP_CREATED:
return R.string.event_title_backup_created;
case VAULT_ANDROID_BACKUP_CREATED:
return R.string.event_title_android_backup_created;
case VAULT_EXPORTED:
return R.string.event_title_vault_exported;
case ENTRY_SHARED:
return R.string.event_title_entry_shared;
case VAULT_UNLOCK_FAILED_PASSWORD:
return R.string.event_title_vault_unlock_failed_password;
case VAULT_UNLOCK_FAILED_BIOMETRICS:
return R.string.event_title_vault_unlock_failed_biometrics;
default:
return R.string.event_unknown;
}
}
}

View file

@ -0,0 +1,15 @@
package com.beemdevelopment.aegis.database;
import android.content.Context;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Database(entities = {AuditLogEntry.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract AuditLogDao auditLogDao();
}

View file

@ -0,0 +1,17 @@
package com.beemdevelopment.aegis.database;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import java.util.List;
@Dao
public interface AuditLogDao {
@Insert
void insert(AuditLogEntry log);
@Query("SELECT * FROM audit_logs WHERE timestamp >= strftime('%s', 'now', '-30 days') ORDER BY timestamp DESC")
LiveData<List<AuditLogEntry>> getAll();
}

View file

@ -0,0 +1,61 @@
package com.beemdevelopment.aegis.database;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import com.beemdevelopment.aegis.EventType;
@Entity(tableName = "audit_logs")
public class AuditLogEntry {
@PrimaryKey(autoGenerate = true)
protected long id;
@NonNull
@ColumnInfo(name = "event_type")
private final EventType _eventType;
@ColumnInfo(name = "reference")
private final String _reference;
@ColumnInfo(name = "timestamp")
private final long _timestamp;
@Ignore
public AuditLogEntry(@NonNull EventType eventType) {
this(eventType, null);
}
@Ignore
public AuditLogEntry(@NonNull EventType eventType, @Nullable String reference) {
_eventType = eventType;
_reference = reference;
_timestamp = System.currentTimeMillis();
}
AuditLogEntry(long id, @NonNull EventType eventType, @Nullable String reference, long timestamp) {
this.id = id;
_eventType = eventType;
_reference = reference;
_timestamp = timestamp;
}
public long getId() {
return id;
}
public EventType getEventType() {
return _eventType;
}
public String getReference() {
return _reference;
}
public long getTimestamp() {
return _timestamp;
}
}

View file

@ -0,0 +1,66 @@
package com.beemdevelopment.aegis.database;
import androidx.lifecycle.LiveData;
import com.beemdevelopment.aegis.EventType;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class AuditLogRepository {
private final AuditLogDao _auditLogDao;
private final Executor _executor;
public AuditLogRepository(AuditLogDao auditLogDao) {
_auditLogDao = auditLogDao;
_executor = Executors.newSingleThreadExecutor();
}
public LiveData<List<AuditLogEntry>> getAllAuditLogEntries() {
return _auditLogDao.getAll();
}
public void addVaultUnlockedEvent() {
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_UNLOCKED);
insert(auditLogEntry);
}
public void addBackupCreatedEvent() {
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_BACKUP_CREATED);
insert(auditLogEntry);
}
public void addAndroidBackupCreatedEvent() {
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_ANDROID_BACKUP_CREATED);
insert(auditLogEntry);
}
public void addVaultExportedEvent() {
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_EXPORTED);
insert(auditLogEntry);
}
public void addEntrySharedEvent(String reference) {
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.ENTRY_SHARED, reference);
insert(auditLogEntry);
}
public void addVaultUnlockFailedPasswordEvent() {
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_UNLOCK_FAILED_PASSWORD);
insert(auditLogEntry);
}
public void addVaultUnlockFailedBiometricsEvent() {
AuditLogEntry auditLogEntry = new AuditLogEntry(EventType.VAULT_UNLOCK_FAILED_BIOMETRICS);
insert(auditLogEntry);
}
public void insert(AuditLogEntry auditLogEntry) {
_executor.execute(() -> {
_auditLogDao.insert(auditLogEntry);
});
}
}

View file

@ -21,6 +21,7 @@ import androidx.core.view.ViewPropertyAnimatorCompat;
import com.beemdevelopment.aegis.Preferences;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.ThemeMap;
import com.beemdevelopment.aegis.database.AuditLogRepository;
import com.beemdevelopment.aegis.helpers.ThemeHelper;
import com.beemdevelopment.aegis.icons.IconPackManager;
import com.beemdevelopment.aegis.vault.VaultManager;
@ -48,6 +49,9 @@ public abstract class AegisActivity extends AppCompatActivity implements VaultMa
@Inject
protected VaultManager _vaultManager;
@Inject
protected AuditLogRepository _auditLogRepository;
@Inject
protected IconPackManager _iconPackManager;

View file

@ -344,6 +344,8 @@ public class AuthActivity extends AegisActivity {
finish(result.getKey(), result.isSlotRepaired());
} else {
_decryptButton.setEnabled(true);
_auditLogRepository.addVaultUnlockFailedPasswordEvent();
onInvalidPassword();
}
}
@ -356,6 +358,7 @@ public class AuthActivity extends AegisActivity {
_bioPrompt = null;
if (!BiometricsHelper.isCanceled(errorCode)) {
_auditLogRepository.addVaultUnlockFailedBiometricsEvent();
Toast.makeText(AuthActivity.this, errString, Toast.LENGTH_LONG).show();
}
}

View file

@ -522,6 +522,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
}
private void onDecryptResult() {
_auditLogRepository.addVaultUnlockedEvent();
loadEntries();
checkTimeSyncSetting();
}
@ -1176,6 +1178,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
for (VaultEntry entry : _selectedEntries) {
GoogleAuthInfo authInfo = new GoogleAuthInfo(entry.getInfo(), entry.getName(), entry.getIssuer());
authInfos.add(authInfo);
_auditLogRepository.addEntrySharedEvent(entry.getUUID().toString());
}
intent.putExtra("authInfos", authInfos);

View file

@ -0,0 +1,97 @@
package com.beemdevelopment.aegis.ui.fragments.preferences;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.View;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.Observer;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.database.AuditLogEntry;
import com.beemdevelopment.aegis.database.AuditLogRepository;
import com.beemdevelopment.aegis.helpers.MetricsHelper;
import com.beemdevelopment.aegis.ui.models.AuditLogEntryModel;
import com.beemdevelopment.aegis.ui.views.AuditLogAdapter;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.beemdevelopment.aegis.vault.VaultManager;
import java.util.List;
import java.util.UUID;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint
public class AuditLogPreferencesFragment extends Fragment {
@Inject
AuditLogRepository _auditLogRepository;
private AuditLogAdapter _adapter;
private RecyclerView _auditLogRecyclerView;
private LinearLayout _noAuditLogsView;
@Inject
VaultManager _vaultManager;
public AuditLogPreferencesFragment() {
super(R.layout.fragment_audit_log);
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
LiveData<List<AuditLogEntry>> entries = _auditLogRepository.getAllAuditLogEntries();
_adapter = new AuditLogAdapter();
_noAuditLogsView = view.findViewById(R.id.vEmptyList);
_auditLogRecyclerView = view.findViewById(R.id.list_audit_log);
LinearLayoutManager layoutManager = new LinearLayoutManager(requireContext());
_auditLogRecyclerView.addItemDecoration(new SpacesItemDecoration(8));
_auditLogRecyclerView.setLayoutManager(layoutManager);
_auditLogRecyclerView.setAdapter(_adapter);
_auditLogRecyclerView.setNestedScrollingEnabled(false);
entries.observe(getViewLifecycleOwner(), entries1 -> {
_noAuditLogsView.setVisibility(entries1.isEmpty() ? View.VISIBLE : View.GONE);
for (AuditLogEntry entry : entries1) {
VaultEntry referencedEntry = null;
if (entry.getReference() != null) {
referencedEntry = _vaultManager.getVault().getEntryByUUID(UUID.fromString(entry.getReference()));
}
AuditLogEntryModel auditLogEntryModel = new AuditLogEntryModel(entry, referencedEntry);
_adapter.addAuditLogEntryModel(auditLogEntryModel);
}
});
}
private class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private final int _space;
public SpacesItemDecoration(int dpSpace) {
_space = MetricsHelper.convertDpToPixels(getContext(), dpSpace);
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
outRect.left = _space;
outRect.right = _space;
outRect.bottom = _space;
if (parent.getChildLayoutPosition(view) == 0) {
outRect.top = _space;
}
}
}
}

View file

@ -23,6 +23,7 @@ import androidx.preference.Preference;
import com.beemdevelopment.aegis.BuildConfig;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.database.AuditLogRepository;
import com.beemdevelopment.aegis.helpers.DropdownHelper;
import com.beemdevelopment.aegis.importers.DatabaseImporter;
import com.beemdevelopment.aegis.otp.GoogleAuthInfo;
@ -63,6 +64,7 @@ import java.util.UUID;
import java.util.stream.Collectors;
import javax.crypto.Cipher;
import javax.inject.Inject;
public class ImportExportPreferencesFragment extends PreferencesFragment {
// keep a reference to the type of database converter that was selected
@ -277,6 +279,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
.setType(getExportMimeType(getExportRequestCode(pos, encrypt)))
.putExtra(Intent.EXTRA_TITLE, fileInfo.toString());
_auditLogRepository.addVaultExportedEvent();
ActivityResultLauncher<Intent> resultLauncher = getExportRequestLauncher(pos, encrypt);
_vaultManager.fireIntentLauncher(this, intent, resultLauncher);
});
@ -320,7 +323,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
// if the user creates an export, hide the backup reminder
_prefs.setLatestExportTimeNow();
_auditLogRepository.addVaultExportedEvent();
Uri uri = FileProvider.getUriForFile(requireContext(), BuildConfig.FILE_PROVIDER_AUTHORITY, file);
Intent intent = new Intent(Intent.ACTION_SEND)
.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
@ -559,7 +562,7 @@ public class ImportExportPreferencesFragment extends PreferencesFragment {
} else {
// if the user creates an export, hide the backup reminder
_prefs.setLatestExportTimeNow();
_auditLogRepository.addVaultExportedEvent();
Toast.makeText(requireContext(), getString(R.string.exported_vault), Toast.LENGTH_SHORT).show();
}
}

View file

@ -13,6 +13,7 @@ import androidx.preference.PreferenceFragmentCompat;
import com.beemdevelopment.aegis.Preferences;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.database.AuditLogRepository;
import com.beemdevelopment.aegis.helpers.AnimationsHelper;
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
import com.beemdevelopment.aegis.vault.VaultManager;
@ -38,6 +39,9 @@ public abstract class PreferencesFragment extends PreferenceFragmentCompat {
@Inject
VaultManager _vaultManager;
@Inject
protected AuditLogRepository _auditLogRepository;
@Override
@CallSuper
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {

View file

@ -0,0 +1,24 @@
package com.beemdevelopment.aegis.ui.models;
import com.beemdevelopment.aegis.database.AuditLogEntry;
import com.beemdevelopment.aegis.vault.VaultEntry;
import javax.annotation.Nullable;
public class AuditLogEntryModel {
private AuditLogEntry _auditLogEntry;
private VaultEntry _referencedVaultEntry;
public AuditLogEntryModel(AuditLogEntry auditLogEntry, @Nullable VaultEntry referencedVaultEntry) {
_auditLogEntry = auditLogEntry;
_referencedVaultEntry = referencedVaultEntry;
}
public AuditLogEntry getAuditLogEntry() {
return _auditLogEntry;
}
public VaultEntry getReferencedVaultEntry() {
return _referencedVaultEntry;
}
}

View file

@ -0,0 +1,62 @@
package com.beemdevelopment.aegis.ui.views;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.database.AuditLogEntry;
import com.beemdevelopment.aegis.ui.models.AuditLogEntryModel;
import com.beemdevelopment.aegis.vault.VaultEntry;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public class AuditLogAdapter extends RecyclerView.Adapter<AuditLogHolder> {
private List<AuditLogEntryModel> _auditLogEntryModels;
private List<VaultEntry> _referencedEntries;
public AuditLogAdapter() {
_auditLogEntryModels = new ArrayList<>();
_referencedEntries = new ArrayList<>();
}
public void addAuditLogEntryModel(AuditLogEntryModel auditLogEntryModel) {
_auditLogEntryModels.add(auditLogEntryModel);
int position = getItemCount() - 1;
if (position == 0) {
notifyDataSetChanged();
} else {
notifyItemInserted(position);
}
}
public void addReferencedEntry(VaultEntry vaultEntry) {
_referencedEntries.add(vaultEntry);
}
@NonNull
@Override
public AuditLogHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.card_audit_log, parent, false);
return new AuditLogHolder(view);
}
@Override
public void onBindViewHolder(@NonNull AuditLogHolder holder, int position) {
AuditLogEntryModel auditLogEntryModel = _auditLogEntryModels.get(position);
VaultEntry referencedEntry = null;
holder.setData(auditLogEntryModel);
}
@Override
public int getItemCount() {
return _auditLogEntryModels.size();
}
}

View file

@ -0,0 +1,140 @@
package com.beemdevelopment.aegis.ui.views;
import android.content.Context;
import android.content.res.ColorStateList;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.cardview.widget.CardView;
import androidx.recyclerview.widget.RecyclerView;
import com.beemdevelopment.aegis.EventType;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.database.AuditLogEntry;
import com.beemdevelopment.aegis.ui.models.AuditLogEntryModel;
import com.beemdevelopment.aegis.vault.VaultEntry;
import com.google.android.material.color.MaterialColors;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
public class AuditLogHolder extends RecyclerView.ViewHolder {
private final CardView _cardView;
private final ImageView _auditLogEntryIcon;
private final TextView _auditLogEntryTitle;
private final TextView _auditLogEntryDescription;
private final TextView _auditLogEntryReference;
private final TextView _auditLogEntryTimestamp;
private final int _errorBackgroundColor;
private final ColorStateList _initialBackgroundColor;
private final int _initialIconColor;
public AuditLogHolder(final View view) {
super(view);
_cardView = (CardView)view;
_auditLogEntryIcon = view.findViewById(R.id.iv_icon_view);
_auditLogEntryTitle = view.findViewById(R.id.text_audit_log_title);
_auditLogEntryDescription = view.findViewById(R.id.text_audit_log_description);
_auditLogEntryReference = view.findViewById(R.id.text_audit_log_reference);
_auditLogEntryTimestamp = view.findViewById(R.id.text_audit_log_timestamp);
_initialBackgroundColor = _cardView.getCardBackgroundColor();
_initialIconColor = MaterialColors.getColor(view, com.google.android.material.R.attr.colorTertiaryContainer);
_errorBackgroundColor = MaterialColors.getColor(view, com.google.android.material.R.attr.colorErrorContainer);
}
public void setData(AuditLogEntryModel auditLogEntryModel) {
AuditLogEntry auditLogEntry = auditLogEntryModel.getAuditLogEntry();
_auditLogEntryIcon.setImageResource(getIconResource(auditLogEntry.getEventType()));
_auditLogEntryTitle.setText(EventType.getEventTitleRes(auditLogEntry.getEventType()));
_auditLogEntryDescription.setText(getEventTypeDescriptionRes(auditLogEntry.getEventType()));
_auditLogEntryTimestamp.setText(formatTimestamp(_cardView.getContext(), auditLogEntry.getTimestamp()).toLowerCase());
if (auditLogEntryModel.getReferencedVaultEntry() != null) {
VaultEntry referencedVaultEntry = auditLogEntryModel.getReferencedVaultEntry();
_auditLogEntryReference.setText(String.format("%s (%s)", referencedVaultEntry.getIssuer(), referencedVaultEntry.getName()));
} else {
_auditLogEntryReference.setVisibility(View.GONE);
}
setCardBackgroundColor(auditLogEntry.getEventType());
}
private void setCardBackgroundColor(EventType eventType) {
if (eventType == EventType.VAULT_UNLOCK_FAILED_PASSWORD || eventType == EventType.VAULT_UNLOCK_FAILED_BIOMETRICS) {
_cardView.setCardBackgroundColor(_errorBackgroundColor);
_auditLogEntryIcon.setBackgroundColor(_errorBackgroundColor);
} else {
_cardView.setCardBackgroundColor(_initialBackgroundColor);
_auditLogEntryIcon.setBackgroundColor(_initialIconColor);
}
}
private int getEventTypeDescriptionRes(EventType eventType) {
switch (eventType) {
case VAULT_UNLOCKED:
return R.string.event_description_vault_unlocked;
case VAULT_BACKUP_CREATED:
return R.string.event_description_backup_created;
case VAULT_ANDROID_BACKUP_CREATED:
return R.string.event_description_android_backup_created;
case VAULT_EXPORTED:
return R.string.event_description_vault_exported;
case ENTRY_SHARED:
return R.string.event_description_entry_shared;
case VAULT_UNLOCK_FAILED_PASSWORD:
return R.string.event_description_vault_unlock_failed_password;
case VAULT_UNLOCK_FAILED_BIOMETRICS:
return R.string.event_description_vault_unlock_failed_biometrics;
default:
return R.string.event_unknown;
}
}
private int getIconResource(EventType eventType) {
switch(eventType) {
case VAULT_UNLOCKED:
return R.drawable.ic_lock_open;
case VAULT_BACKUP_CREATED:
case VAULT_ANDROID_BACKUP_CREATED:
return R.drawable.ic_folder_zip;
case VAULT_EXPORTED:
return R.drawable.ic_export_notes;
case ENTRY_SHARED:
return R.drawable.ic_share;
case VAULT_UNLOCK_FAILED_PASSWORD:
case VAULT_UNLOCK_FAILED_BIOMETRICS:
return R.drawable.ic_lock;
}
return -1;
}
private static String formatTimestamp(Context context, long epochMilli) {
LocalDateTime now = LocalDateTime.now();
LocalDateTime timestamp = LocalDateTime.ofInstant(Instant.ofEpochMilli(epochMilli), ZoneId.systemDefault());
long daysBetween = ChronoUnit.DAYS.between(timestamp.toLocalDate(), now.toLocalDate());
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("h:mm a");
if (daysBetween < 1) {
String formattedTime = timestamp.format(timeFormatter);
return context.getString(R.string.today_at_time, formattedTime);
} else if (daysBetween < 7) {
DateTimeFormatter dayOfWeekFormatter = DateTimeFormatter.ofPattern("EEEE");
String dayOfWeek = timestamp.format(dayOfWeekFormatter);
String formattedTime = timestamp.format(timeFormatter);
return context.getString(R.string.day_of_week_at_time, dayOfWeek, formattedTime);
} else {
DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(context.getString(R.string.date_format));
return timestamp.format(dateFormatter);
}
}
}

View file

@ -10,6 +10,7 @@ import androidx.annotation.NonNull;
import androidx.documentfile.provider.DocumentFile;
import com.beemdevelopment.aegis.Preferences;
import com.beemdevelopment.aegis.database.AuditLogRepository;
import com.beemdevelopment.aegis.util.IOUtils;
import java.io.File;
@ -41,17 +42,20 @@ public class VaultBackupManager {
private final Context _context;
private final Preferences _prefs;
private final ExecutorService _executor;
private final AuditLogRepository _auditLogRepository;
public VaultBackupManager(Context context) {
public VaultBackupManager(Context context, AuditLogRepository auditLogRepository) {
_context = context;
_prefs = new Preferences(context);
_executor = Executors.newSingleThreadExecutor();
_auditLogRepository = auditLogRepository;
}
public void scheduleBackup(File tempFile, Uri dirUri, int versionsToKeep) {
_executor.execute(() -> {
try {
createBackup(tempFile, dirUri, versionsToKeep);
_auditLogRepository.addBackupCreatedEvent();
_prefs.setBuiltInBackupResult(new Preferences.BackupResult(null));
} catch (VaultRepositoryException | VaultBackupPermissionException e) {
e.printStackTrace();

View file

@ -15,6 +15,7 @@ import com.beemdevelopment.aegis.Preferences;
import com.beemdevelopment.aegis.R;
import com.beemdevelopment.aegis.crypto.KeyStoreHandle;
import com.beemdevelopment.aegis.crypto.KeyStoreHandleException;
import com.beemdevelopment.aegis.database.AuditLogRepository;
import com.beemdevelopment.aegis.services.NotificationService;
import com.beemdevelopment.aegis.ui.dialogs.Dialogs;
@ -37,12 +38,15 @@ public class VaultManager {
private final List<LockListener> _lockListeners;
private boolean _blockAutoLock;
public VaultManager(@NonNull Context context) {
private final AuditLogRepository _auditLogRepository;
public VaultManager(@NonNull Context context, AuditLogRepository auditLogRepository) {
_context = context;
_prefs = new Preferences(_context);
_backups = new VaultBackupManager(_context);
_backups = new VaultBackupManager(_context, auditLogRepository);
_androidBackups = new BackupManager(context);
_lockListeners = new ArrayList<>();
_auditLogRepository = auditLogRepository;
}
/**