mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-06-07 23:27:50 +00:00
Start working on audit logs
This commit is contained in:
parent
3c124deae1
commit
171da34b13
31 changed files with 834 additions and 7 deletions
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
42
app/src/main/java/com/beemdevelopment/aegis/EventType.java
Normal file
42
app/src/main/java/com/beemdevelopment/aegis/EventType.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue