Skip to content

Commit

Permalink
refactoring, add tooltips, adjust strings, build WOT audit log
Browse files Browse the repository at this point in the history
  • Loading branch information
iammajid committed Jan 28, 2025
1 parent cf49107 commit 83be444
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 54 deletions.
9 changes: 8 additions & 1 deletion frontend/src/common/auditlog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ export type AuditEventDeviceRemoveDto = AuditEventDtoBase & {
deviceId: string;
}

export type AuditEventSettingWotUpdateDto = AuditEventDtoBase & {
type: 'SETTING_WOT_UPDATE',
updatedBy: string;
wotMaxDepth: number;
wotIdVerifyLen: number;
}

export type AuditEventSignedWotIdDto = AuditEventDtoBase & {
type: 'SIGN_WOT_ID',
userId: string;
Expand Down Expand Up @@ -89,7 +96,7 @@ export type AuditEventVaultOwnershipClaimDto = AuditEventDtoBase & {
vaultId: string;
}

export type AuditEventDto = AuditEventDeviceRegisterDto | AuditEventDeviceRemoveDto | AuditEventSignedWotIdDto | AuditEventVaultCreateDto | AuditEventVaultUpdateDto | AuditEventVaultAccessGrantDto | AuditEventVaultKeyRetrieveDto | AuditEventVaultMemberAddDto | AuditEventVaultMemberRemoveDto | AuditEventVaultMemberUpdateDto | AuditEventVaultOwnershipClaimDto;
export type AuditEventDto = AuditEventDeviceRegisterDto | AuditEventDeviceRemoveDto | AuditEventSettingWotUpdateDto | AuditEventSignedWotIdDto | AuditEventVaultCreateDto | AuditEventVaultUpdateDto | AuditEventVaultAccessGrantDto | AuditEventVaultKeyRetrieveDto | AuditEventVaultMemberAddDto | AuditEventVaultMemberRemoveDto | AuditEventVaultMemberUpdateDto | AuditEventVaultOwnershipClaimDto;

/* Entity Cache */

Expand Down
97 changes: 53 additions & 44 deletions frontend/src/components/AdminSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,28 +120,13 @@
<XMarkIcon class="shrink-0 text-red-500 mr-1 h-5 w-5" aria-hidden="true" />
{{ t('admin.licenseInfo.expiresAt.description.expired') }}
</p>
</div>

<div class="col-span-6 sm:col-span-3">
<label for="trustLevel" class="block text-sm font-medium text-gray-700">{{ t('admin.webOfTrust.trustLevel.title') }}</label>
<input id="trustLevel" v-model="trustLevel" type="number" min="0" max="10" step="1" class="mt-1 block w-full shadow-sm sm:text-sm rounded-md border-gray-300 focus:ring-primary focus:border-primary [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" :class="{ 'invalid:border-red-300 invalid:text-red-900 focus:invalid:ring-red-500 focus:invalid:border-red-500': trustLevelError }" />
<p v-if="trustLevelError" class="mt-1 text-sm text-red-900">
{{ t('admin.webOfTrust.trustLevel.error') }}
</p>
</div>

<div class="col-span-6 sm:col-span-3">
<label for="maxChainLength" class="block text-sm font-medium text-gray-700">{{ t('admin.webOfTrust.maxChainLength.title') }}</label>
<input id="maxChainLength" v-model="maxChainLength" type="number" min="0" step="1" class="mt-1 block w-full shadow-sm sm:text-sm rounded-md border-gray-300 focus:ring-primary focus:border-primary [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" :class="{ 'invalid:border-red-300 invalid:text-red-900 focus:invalid:ring-red-500 focus:invalid:border-red-500': maxChainLengthError }" />
<p v-if="maxChainLengthError" class="mt-1 text-sm text-red-900">
{{ t('admin.webOfTrust.maxChainLength.error') }}
</p>
</div>

<div class="col-span-6 flex justify-end space-x-3">
<button type="button" class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-primary hover:bg-primary-d1 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary" @click="saveWebOfTrust">
{{ t('admin.webOfTrust.save') }}
</button>
<div v-if="admin.hasLicense" class="col-span-6 flex justify-end space-x-3">
<button type="button" class="flex-none inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-primary hover:bg-primary-d1 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary disabled:opacity-50 disabled:hover:bg-primary disabled:cursor-not-allowed" @click="manageSubscription()">
<ArrowTopRightOnSquareIcon class="-ml-1 mr-2 h-5 w-5" aria-hidden="true" />
{{ t('admin.licenseInfo.manageSubscription') }}
</button>
</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -207,25 +192,49 @@
{{ t('admin.webOfTrust.description') }}
</p>
</div>
<div class="mt-5 md:mt-0 md:col-span-2">
<form class="mt-5 md:mt-0 md:col-span-2">
<div class="grid grid-cols-6 gap-6">
<div class="col-span-6 sm:col-span-3">
<label for="trustLevel" class="block text-sm font-medium text-gray-700">
{{ t('admin.webOfTrust.trustLevel.title') }}
<label for="wotMaxDepth" class="block text-sm font-medium text-gray-700 flex items-center">
{{ t('admin.webOfTrust.wotMaxDepth.title') }}
<div class="relative group ml-1">
<InformationCircleIcon class="shrink-0 text-primary h-5 w-5 cursor-pointer" aria-hidden="true" />
<div class="absolute hidden group-hover:block bg-gray-600 text-white text-xs rounded px-2 py-2 left-1/2 bottom-full z-10" style="white-space: normal; width: 350px; transform: translateX(-50%); margin-bottom: 0.5rem; pointer-events: auto;">
{{ t('admin.webOfTrust.wotMaxDepth.description') }}
<div class="mt-2">
<a href="https://docs.cryptomator.org/en/latest/security/hub/#web-of-trust" target="_blank" class="text-primary underline hover:text-primary-darker">
{{ t('admin.webOfTrust.wotMaxDepth.information') }}
</a>
</div>
<div class="absolute left-1/2 translate-x-[-50%] top-full w-0 h-0 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-t-[6px] border-t-gray-600"></div>
</div>
</div>
</label>
<input id="trustLevel" v-model="trustLevel" type="number" min="0" max="10" step="1" class="mt-1 block w-full shadow-sm sm:text-sm rounded-md border-gray-300 focus:ring-primary focus:border-primary [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" :class="{ 'invalid:border-red-300 invalid:text-red-900 focus:invalid:ring-red-500 focus:invalid:border-red-500': trustLevelError }" />
<p v-if="trustLevelError" class="mt-1 text-sm text-red-900">
{{ t('admin.webOfTrust.trustLevel.error') }}
<input id="wotMaxDepth" v-model="wotMaxDepth" type="number" min="0" max="10" step="1" class="mt-1 block w-full shadow-sm sm:text-sm rounded-md border-gray-300 focus:ring-primary focus:border-primary [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" :class="{ 'invalid:border-red-300 invalid:text-red-900 focus:invalid:ring-red-500 focus:invalid:border-red-500': wotMaxDepthError }" />
<p v-if="wotMaxDepthError" class="mt-1 text-sm text-red-900">
{{ t('admin.webOfTrust.wotMaxDepth.error') }}
</p>
</div>

<div class="col-span-6 sm:col-span-3">
<label for="maxChainLength" class="block text-sm font-medium text-gray-700">
{{ t('admin.webOfTrust.maxChainLength.title') }}
<label for="wotIdVerifyLen" class="block text-sm font-medium text-gray-700 flex items-center">
{{ t('admin.webOfTrust.wotIdVerifyLen.title') }}
<div class="relative group ml-1">
<InformationCircleIcon class="shrink-0 text-primary h-5 w-5 cursor-pointer" aria-hidden="true" />
<div class="absolute hidden group-hover:block bg-gray-600 text-white text-xs rounded px-2 py-2 left-1/2 bottom-full z-10" style="white-space: normal; width: 350px; transform: translateX(-50%); margin-bottom: 0.5rem; pointer-events: auto;">
{{ t('admin.webOfTrust.wotIdVerifyLen.description') }}
<div class="mt-2">
<a href="https://docs.cryptomator.org/en/latest/security/hub/#web-of-trust" target="_blank" class="text-primary underline hover:text-primary-darker">
{{ t('admin.webOfTrust.wotIdVerifyLen.information') }}
</a>
</div>
<div class="absolute left-1/2 translate-x-[-50%] top-full w-0 h-0 border-l-[6px] border-l-transparent border-r-[6px] border-r-transparent border-t-[6px] border-t-gray-600"></div>
</div>
</div>
</label>
<input id="maxChainLength" v-model="maxChainLength" type="number" min="0" step="1" class="mt-1 block w-full shadow-sm sm:text-sm rounded-md border-gray-300 focus:ring-primary focus:border-primary [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" :class="{ 'invalid:border-red-300 invalid:text-red-900 focus:invalid:ring-red-500 focus:invalid:border-red-500': maxChainLengthError }" />
<p v-if="maxChainLengthError" class="mt-1 text-sm text-red-900">
{{ t('admin.webOfTrust.maxChainLength.error') }}
<input id="wotIdVerifyLen" v-model="wotIdVerifyLen" type="number" min="0" step="1" class="mt-1 block w-full shadow-sm sm:text-sm rounded-md border-gray-300 focus:ring-primary focus:border-primary [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none" :class="{ 'invalid:border-red-300 invalid:text-red-900 focus:invalid:ring-red-500 focus:invalid:border-red-500': wotIdVerifyLenError }" />
<p v-if="wotIdVerifyLenError" class="mt-1 text-sm text-red-900">
{{ t('admin.webOfTrust.wotIdVerifyLen.error') }}
</p>
</div>

Expand All @@ -238,7 +247,7 @@
</button>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
Expand Down Expand Up @@ -269,12 +278,12 @@ const now = ref<Date>(new Date());
const keycloakAdminRealmURL = ref<string>();
const onFetchError = ref<Error | null>();
const errorOnFetchingUpdates = ref<boolean>(false);
const trustLevel = ref<number>(0);
const maxChainLength = ref<number>(0);
const wotMaxDepth = ref<number>(0);
const wotIdVerifyLen = ref<number>(0);
// Add validation refs
const trustLevelError = ref(false);
const maxChainLengthError = ref(false);
const wotMaxDepthError = ref(false);
const wotIdVerifyLenError = ref(false);
const showErrors = ref(false); // New ref to control error display
const isBeta = computed(() => {
Expand Down Expand Up @@ -331,8 +340,8 @@ async function fetchData() {
// Get the settings and set the values
const settings = await backend.settings.get();
trustLevel.value = settings.wotMaxDepth;
maxChainLength.value = settings.wotIdVerifyLen;
wotMaxDepth.value = settings.wotMaxDepth;
wotIdVerifyLen.value = settings.wotIdVerifyLen;
} catch (error) {
if (error instanceof FetchUpdateError) {
errorOnFetchingUpdates.value = true;
Expand Down Expand Up @@ -372,17 +381,17 @@ const processing = ref(false);
async function saveWebOfTrust() {
onSaveError.value = null;
trustLevelError.value = trustLevel.value < 0 || trustLevel.value > 10;
maxChainLengthError.value = maxChainLength.value < 0;
wotMaxDepthError.value = wotMaxDepth.value < 0 || wotMaxDepth.value > 10;
wotIdVerifyLenError.value = wotIdVerifyLen.value < 0;
if (trustLevelError.value || maxChainLengthError.value) {
if (wotMaxDepthError.value || wotIdVerifyLenError.value) {
return;
}
try {
processing.value = true;
const settings = {
wotMaxDepth: trustLevel.value,
wotIdVerifyLen: maxChainLength.value,
wotMaxDepth: wotMaxDepth.value,
wotIdVerifyLen: wotIdVerifyLen.value,
hubId: admin.value?.hubId ?? ''
};
await backend.settings.put(settings);
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/components/AuditLog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@
</td>
<AuditLogDetailsDeviceRegister v-if="auditEvent.type == 'DEVICE_REGISTER'" :event="auditEvent" />
<AuditLogDetailsDeviceRemove v-else-if="auditEvent.type == 'DEVICE_REMOVE'" :event="auditEvent" />
<AuditLogDetailsSettingWotUpdate v-else-if="auditEvent.type == 'SETTING_WOT_UPDATE'" :event="auditEvent" />
<AuditLogDetailsSignedWotId v-else-if="auditEvent.type == 'SIGN_WOT_ID'" :event="auditEvent" />
<AuditLogDetailsVaultCreate v-else-if="auditEvent.type == 'VAULT_CREATE'" :event="auditEvent" />
<AuditLogDetailsVaultUpdate v-else-if="auditEvent.type == 'VAULT_UPDATE'" :event="auditEvent" />
Expand Down Expand Up @@ -169,6 +170,7 @@ import auditlog, { AuditEventDto } from '../common/auditlog';
import { PaymentRequiredError } from '../common/backend';
import AuditLogDetailsDeviceRegister from './AuditLogDetailsDeviceRegister.vue';
import AuditLogDetailsDeviceRemove from './AuditLogDetailsDeviceRemove.vue';
import AuditLogDetailsSettingWotUpdate from './AuditLogDetailsSettingWotUpdate.vue';
import AuditLogDetailsSignedWotId from './AuditLogDetailsSignedWotId.vue';
import AuditLogDetailsVaultAccessGrant from './AuditLogDetailsVaultAccessGrant.vue';
import AuditLogDetailsVaultCreate from './AuditLogDetailsVaultCreate.vue';
Expand Down
53 changes: 53 additions & 0 deletions frontend/src/components/AuditLogDetailsSettingWotUpdate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<template>
<td class="whitespace-nowrap px-3 py-4 text-sm font-medium text-gray-900">
{{ t('auditLog.details.setting.wot.update') }}
</td>
<td class="whitespace-nowrap py-4 pl-3 pr-4 sm:pr-6">
<dl class="flex flex-col gap-2">
<div class="flex items-baseline gap-2">
<dt class="text-xs text-gray-500">
<code>registered by</code>
</dt>
<dd class="flex items-baseline gap-2 text-sm text-gray-900">
<span v-if="resolvedUpdatedBy != null">{{ resolvedUpdatedBy.name }}</span>
<code class="text-xs" :class="{'text-gray-600': resolvedUpdatedBy != null}">{{ event.updatedBy }}</code>
</dd>
</div>
<div class="flex items-baseline gap-2">
<dt class="text-xs text-gray-500">
<code>maximum wot depth</code>
</dt>
<dd class="flex items-baseline gap-2 text-sm text-gray-900">
<code class="text-xs text-gray-600">{{ event.wotMaxDepth }}</code>
</dd>
</div>
<div class="flex items-baseline gap-2">
<dt class="text-xs text-gray-500">
<code>fingerprint verification preciseness</code>
</dt>
<dd class="flex items-baseline gap-2 text-sm text-gray-900">
<code class="text-xs text-gray-600">{{ event.wotIdVerifyLen }}</code>
</dd>
</div>
</dl>
</td>
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import auditlog, { AuditEventSettingWotUpdateDto } from '../common/auditlog';
import { AuthorityDto } from '../common/backend';
const { t } = useI18n({ useScope: 'global' });
const props = defineProps<{
event: AuditEventSettingWotUpdateDto
}>();
const resolvedUpdatedBy = ref<AuthorityDto>();
onMounted(async () => {
resolvedUpdatedBy.value = await auditlog.entityCache.getAuthority(props.event.updatedBy);
});
</script>
15 changes: 10 additions & 5 deletions frontend/src/i18n/de-DE.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,15 @@
"admin.licenseInfo.selfHostedNoLicense.description": "Vielen Dank, dass du Cryptomator Hub nutzt! Du hast die Community-Lizenz erhalten. Wenn du mehr Sitze benötigst, erweiter deine Lizenz.",
"admin.licenseInfo.managedNoLicense.description": "Vielen Dank, dass du Cryptomator Hub nutzt! Du hast aktuell keine aktive Lizenz.",
"admin.webOfTrust.title": "Web of Trust",
"admin.webOfTrust.description": "Konfigurieren Sie die Web of Trust-Einstellungen, um zu verwalten, wie Vertrauen zwischen Benutzern im System aufgebaut wird. Diese Einstellungen beeinflussen die Überprüfung von Benutzeridentitäten und Schlüssel-Signaturen.",
"admin.webOfTrust.trustLevel.error": "Das Vertrauensniveau muss zwischen 0 und 10 liegen.",
"admin.webOfTrust.trustLevel.title": "Vertrauensniveau",
"admin.webOfTrust.maxChainLength.error": "Die maximale Kettenlänge muss größer als 0 sein.",
"admin.webOfTrust.maxChainLength.title": "Maximale Kettenlänge",
"admin.webOfTrust.description": "Konfiguriere die Web of Trust-Einstellungen, um zu verwalten, wie Vertrauen zwischen Benutzern im System aufgebaut wird. Diese Einstellungen beeinflussen die Überprüfung von Benutzeridentitäten und Schlüssel-Signaturen.",
"admin.webOfTrust.wotMaxDepth.error": "Die maximale WoT-Tiefe muss zwischen 0 und 10 liegen.",
"admin.webOfTrust.wotMaxDepth.information":"Erfahre mehr über die maximale WoT-Tiefe",
"admin.webOfTrust.wotMaxDepth.title": "Maximale WoT-Tiefe",
"admin.webOfTrust.wotMaxDepth.description": "Maximale Entfernung zwischen zwei Nutzern, sodass eine indirekte Bekanntschaft noch als vertrauenswürdig gilt (vorausgesetzt, die Zwischenidentitäten wurden verifiziert).",
"admin.webOfTrust.wotIdVerifyLen.error": "Die Genauigkeit der Fingerabdrucküberprüfung muss positiv sein.",
"admin.webOfTrust.wotIdVerifyLen.information":"Erfahre mehr über die Fingerabdrucküberprüfung",
"admin.webOfTrust.wotIdVerifyLen.title": "Genauigkeit der Fingerabdrucküberprüfung",
"admin.webOfTrust.wotIdVerifyLen.description": "Wie viele Ziffern des Fingerabdrucks eines anderen Nutzers eingegeben werden müssen, um ihre Identität zu verifizieren.",
"admin.webOfTrust.save": "Speichern",

"archiveVaultDialog.title": "Tresor archivieren",
Expand All @@ -74,6 +78,7 @@
"auditLog.details": "Details",
"auditLog.details.device.register": "Gerät registrieren",
"auditLog.details.device.remove": "Gerät entfernen",
"auditLog.details.setting.wot.update": "WoT-Einstellung aktualisieren",
"auditLog.details.wot.signedIdentity": "Identität beglaubigt",
"auditLog.details.vault.create": "Tresor erstellen",
"auditLog.details.vault.update": "Tresor aktualisieren",
Expand Down
Loading

0 comments on commit 83be444

Please sign in to comment.