Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Build to ESM #291

Merged
merged 6 commits into from
Aug 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix various linter problems
  • Loading branch information
overheadhunter committed Aug 22, 2024
commit cd5d146ab7b6cf19a7b82dbb6eb4dc929cf84035
6 changes: 3 additions & 3 deletions frontend/src/common/auditlog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ export class AuditLogEntityCache {
return this.getEntity<DeviceDto>(deviceId, this.devices, this.debouncedResolvePendingDevices);
}

private async getEntity<T>(entityId: string, entities: Map<string, Deferred<T>>, debouncedResolvePendingEntities: Function): Promise<T> {
private async getEntity<T>(entityId: string, entities: Map<string, Deferred<T>>, debouncedResolvePendingEntities: () => void): Promise<T> {
const cachedEntity = entities.get(entityId);
if (!cachedEntity) {
const deferredEntity = new Deferred<T>();
Expand All @@ -133,8 +133,8 @@ export class AuditLogEntityCache {
private debouncedResolvePendingDevices = debounce(async () => await this.resolvePendingEntities<DeviceDto>(this.devices, backend.devices.listSome), 100);

private async resolvePendingEntities<T extends { id: string }>(entities: Map<string, Deferred<T>>, listSome: (ids: string[]) => Promise<T[]>): Promise<void> {
const pendingEntities = Array.from(entities.entries()).filter(([_, v]) => v.status === 'pending');
const entitiesResult = await listSome(pendingEntities.map(([k, _]) => k));
const pendingEntities = Array.from(entities.entries()).filter(([, v]) => v.status === 'pending');
const entitiesResult = await listSome(pendingEntities.map(([k,]) => k));
for (const [entityId, deferredEntity] of pendingEntities) {
const entity = entitiesResult.find(v => v.id === entityId);
if (entity) {
Expand Down
15 changes: 8 additions & 7 deletions frontend/src/common/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ axiosAuth.interceptors.request.use(async request => {
request.headers = AxiosHeaders.from({ 'Authorization': `Bearer ${token}` });
}
return request;
} catch (err: unknown) {
} catch {
// only things from auth module can throw errors here
throw new UnauthorizedError();
}
Expand Down Expand Up @@ -160,7 +160,7 @@ class VaultService {
public async get(vaultId: string): Promise<VaultDto> {
return axiosAuth.get(`/vaults/${vaultId}`)
.then(response => {
let dateString = response.data.creationTime;
const dateString = response.data.creationTime;
response.data.creationTime = new Date(dateString);
return response.data;
})
Expand Down Expand Up @@ -228,12 +228,12 @@ class DeviceService {
return axiosAuth.get<DeviceDto[]>(`/devices?${query}`).then(response => response.data);
}

public async removeDevice(deviceId: string): Promise<AxiosResponse<any>> {
public async removeDevice(deviceId: string): Promise<AxiosResponse<unknown>> {
return axiosAuth.delete(`/devices/${deviceId}`)
.catch((error) => rethrowAndConvertIfExpected(error, 404));
}

public async putDevice(device: DeviceDto): Promise<AxiosResponse<any>> {
public async putDevice(device: DeviceDto): Promise<AxiosResponse<unknown>> {
return axiosAuth.put(`/devices/${device.id}`, device);
}
}
Expand All @@ -260,6 +260,7 @@ class TrustService {
public async trustUser(userId: string, signature: string): Promise<void> {
return axiosAuth.put(`/users/trusted/${userId}`, signature, { headers: { 'Content-Type': 'text/plain' } });
}

public async get(userId: string): Promise<TrustDto | undefined> {
return axiosAuth.get<TrustDto>(`/users/trusted/${userId}`).then(response => response.data)
.catch(e => {
Expand Down Expand Up @@ -288,9 +289,9 @@ class AuthorityService {
return {
...authority,
pictureUrl: authority.pictureUrl
}
};
} else {
let cfg = AuthorityService.getJdenticonConfig(authority.type);
const cfg = AuthorityService.getJdenticonConfig(authority.type);
const svg = toSvg(authority.id, 100, cfg);
const bytes = new TextEncoder().encode(svg);
const url = `data:image/svg+xml;base64,${base64.stringify(bytes)}`;
Expand Down Expand Up @@ -407,7 +408,7 @@ function convertExpectedToBackendError(status: number): BackendError {
* @param error A thrown object
* @param expectedStatusCodes The expected http status codes of the backend call
*/
export function rethrowAndConvertIfExpected(error: unknown, ...expectedStatusCodes: number[]): Promise<any> {
export function rethrowAndConvertIfExpected(error: unknown, ...expectedStatusCodes: number[]): never {
if (AxiosStatic.isAxiosError(error) && error.response != null && expectedStatusCodes.includes(error.response.status)) {
throw convertExpectedToBackendError(error.response.status);
} else {
Expand Down
5 changes: 2 additions & 3 deletions frontend/src/common/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import { base16, base32, base64, base64url } from 'rfc4648';
import { JWEBuilder, JWEParser } from './jwe';
import { CRC32, DB, wordEncoder } from './util';
export class UnwrapKeyError extends Error {
readonly actualError: any;
readonly actualError: unknown;

constructor(actualError: any) {
constructor(actualError: unknown) {
super('Unwrapping key failed');
this.actualError = actualError;
}
Expand Down Expand Up @@ -270,7 +270,6 @@ export class UserKeys {

public static readonly ECDSA_KEY_DESIGNATION: EcKeyImportParams | EcKeyGenParams = { name: 'ECDSA', namedCurve: 'P-384' };


protected constructor(readonly ecdhKeyPair: CryptoKeyPair, readonly ecdsaKeyPair: CryptoKeyPair) { }

/**
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/common/jwe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ export class JWEParser {
* @param recipientPrivateKey The recipient's private key
* @returns Decrypted payload
*/
public async decryptEcdhEs(recipientPrivateKey: CryptoKey): Promise<any> {
public async decryptEcdhEs<T>(recipientPrivateKey: CryptoKey): Promise<T> {
if (this.header.alg != 'ECDH-ES' || this.header.enc != 'A256GCM' || !this.header.epk) {
throw new Error('unsupported alg or enc');
}
Expand All @@ -96,7 +96,7 @@ export class JWEParser {
* @returns Decrypted payload
* @throws {UnwrapKeyError} if decryption failed (wrong password?)
*/
public async decryptPbes2(password: string): Promise<any> {
public async decryptPbes2<T>(password: string): Promise<T> {
if (this.header.alg != 'PBES2-HS512+A256KW' || /* this.header.enc != 'A256GCM' || */ !this.header.p2s || !this.header.p2c) {
throw new Error('unsupported alg or enc');
}
Expand All @@ -110,7 +110,7 @@ export class JWEParser {
}
}

private async decrypt(cek: CryptoKey): Promise<any> {
private async decrypt<T>(cek: CryptoKey): Promise<T> {
const utf8enc = new TextEncoder();
const m = new Uint8Array(this.ciphertext.length + this.tag.length);
m.set(this.ciphertext, 0);
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/common/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export class JWT {
* @param payload The payload
* @param signerPrivateKey The signers's private key
*/
public static async build(header: JWTHeader, payload: any, signerPrivateKey: CryptoKey): Promise<string> {
public static async build(header: JWTHeader, payload: object, signerPrivateKey: CryptoKey): Promise<string> {
const encodedHeader = base64url.stringify(new TextEncoder().encode(JSON.stringify(header)), { pad: false });
const encodedPayload = base64url.stringify(new TextEncoder().encode(JSON.stringify(payload)), { pad: false });
const encodedSignature = await this.es384sign(encodedHeader, encodedPayload, signerPrivateKey);
Expand Down Expand Up @@ -46,8 +46,8 @@ export class JWT {
* @returns header and payload
* @throws Error if the JWT is invalid
*/
public static async parse(jwt: string, signerPublicKey: CryptoKey): Promise<[JWTHeader, any]> {
const [encodedHeader, encodedPayload, encodedSignature] = jwt.split('.');
public static async parse(jwt: string, signerPublicKey: CryptoKey): Promise<[JWTHeader, object]> {
const [encodedHeader, encodedPayload] = jwt.split('.');
const header: JWTHeader = JSON.parse(new TextDecoder().decode(base64url.parse(encodedHeader, { loose: true })));
if (header.alg !== 'ES384') {
throw new Error('Unsupported algorithm');
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/common/updatecheck.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type LatestVersionDto = {

class UpdatesService {
public async get(localVersion: string): Promise<LatestVersionDto> {
let config = {
const config = {
headers: {
'Content-Type': 'application/json',
'Cryptomator-Hub-Version': localVersion,
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/common/userdata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { BrowserKeys, UserKeys } from './crypto';
import { JWEParser } from './jwe';

class UserData {

#me?: Promise<UserDto>;
#browserKeys?: Promise<BrowserKeys | undefined>;

Expand Down Expand Up @@ -136,7 +135,6 @@ class UserData {
await backend.users.putMe(me);
}
}

}

const instance = new UserData();
Expand Down
7 changes: 4 additions & 3 deletions frontend/src/common/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { dictionary } from './4096words_en';
export class DB {
private static readonly NAME = 'hub';

public static async transaction<T = any>(objectStore: string, mode: IDBTransactionMode, query: (transaction: IDBTransaction) => IDBRequest<T>): Promise<T> {
public static async transaction<T>(objectStore: string, mode: IDBTransactionMode, query: (transaction: IDBTransaction) => IDBRequest<T>): Promise<T> {
const db = await new Promise<IDBDatabase>((resolve, reject) => {
const req = indexedDB.open(DB.NAME);
req.onsuccess = () => resolve(req.result);
Expand All @@ -23,7 +23,7 @@ export class DB {

export class Deferred<T> {
public promise: Promise<T>;
public reject: (reason?: any) => void;
public reject: (reason?: unknown) => void;
public resolve: (value: T) => void;
public status: 'pending' | 'resolved' | 'rejected' = 'pending';

Expand All @@ -43,9 +43,10 @@ export class Deferred<T> {
* @param wait time to wait before calling function
* @returns debounced function
*/
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
export const debounce = (func: Function, wait = 300) => {
let timeoutId: ReturnType<typeof setTimeout>;
function debounceCore(this: any, ...args: any[]) {
function debounceCore(this: unknown, ...args: unknown[]) {
cancel();
timeoutId = setTimeout(() => func.apply(this, args), wait);
}
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/common/wot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ async function sign(user: UserDto): Promise<TrustDto> {
* @param allegedSignedKey The public key that should be signed by the last signature in the chain
*/
async function verify(signatureChain: string[], allegedSignedKey: SignedKeys) {
let signerPublicKey = await userdata.decryptUserKeysWithBrowser().then(keys => keys.ecdsaKeyPair.publicKey);
const signerPublicKey = await userdata.decryptUserKeysWithBrowser().then(keys => keys.ecdsaKeyPair.publicKey);
await verifyRescursive(signatureChain, signerPublicKey, allegedSignedKey);
}

Expand All @@ -62,7 +62,7 @@ async function verify(signatureChain: string[], allegedSignedKey: SignedKeys) {
async function verifyRescursive(signatureChain: string[], signerPublicKey: CryptoKey, allegedSignedKey: SignedKeys) {
// get first element of signature chain:
const [signature, ...remainingChain] = signatureChain;
const [_, signedKeys] = await JWT.parse(signature, signerPublicKey) as [JWTHeader, SignedKeys];
const [, signedKeys] = await JWT.parse(signature, signerPublicKey) as [JWTHeader, SignedKeys];
if (remainingChain.length === 0) {
// last element in chain should match signed public key
if (!deeplyEqual(signedKeys, allegedSignedKey)) {
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/components/AdminSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ const numberOfExceededSeats = computed(() => {
});

onMounted(async () => {
let cfg = config.get();
const cfg = config.get();
keycloakAdminRealmURL.value = `${cfg.keycloakUrl}/admin/${cfg.keycloakRealm}/console`;
if (props.token) {
await setToken(props.token);
Expand All @@ -256,8 +256,8 @@ async function setToken(token: string) {

async function fetchData() {
try {
let versionDto = backend.version.get();
let versionAvailable = versionDto.then(versionDto => updateChecker.get(versionDto.hubVersion));
const versionDto = backend.version.get();
const versionAvailable = versionDto.then(versionDto => updateChecker.get(versionDto.hubVersion));
admin.value = await backend.billing.get();
version.value = await versionDto;
latestVersion.value = await versionAvailable;
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/AuditLogDetailsSignedWotId.vue
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ onMounted(async () => {
try {
const signerPublicKey = await asPublicKey(base64.parse(props.event.signerKey), UserKeys.ECDSA_KEY_DESIGNATION, UserKeys.ECDSA_PUB_KEY_USAGES);
const [_, signedKeys] = await JWT.parse(props.event.signature, signerPublicKey) as [JWTHeader, SignedKeys];
signedFingerprint.value = await wot.computeFingerprint({ecdhPublicKey: signedKeys.ecdhPublicKey, ecdsaPublicKey: signedKeys.ecdsaPublicKey});
signedFingerprint.value = await wot.computeFingerprint({ ecdhPublicKey: signedKeys.ecdhPublicKey, ecdsaPublicKey: signedKeys.ecdsaPublicKey });
if (props.event.signerKey === signingUser?.ecdsaPublicKey && signedFingerprint.value === currentFingerprint.value) {
signatureStatus.value = SignatureStatus.STILL_VALID;
} else if (props.event.signerKey !== signingUser?.ecdsaPublicKey) {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/DeviceList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ async function removeDevice(device: DeviceDto) {
if (error instanceof NotFoundError) {
// if device is already missing in backend → ignore and proceed to then()
} else {
let e = error instanceof Error ? error : new Error('Unknown Error');
const e = error instanceof Error ? error : new Error('Unknown Error');
onRemoveDeviceError.value[device.id] = e;
throw e;
}
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/components/GrantPermissionDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ import backend, { AccessGrant, ConflictError, NotFoundError, TrustDto, UserDto,
import { VaultKeys } from '../common/crypto';
import TrustDetails from './TrustDetails.vue';


const { t } = useI18n({ useScope: 'global' });

const open = ref(false);
Expand Down Expand Up @@ -122,7 +121,7 @@ async function grantAccess() {
}

async function giveUsersAccess(users: UserDto[]) {
let tokens: AccessGrant[] = [];
const tokens: AccessGrant[] = [];
for (const user of users) {
if (user.ecdhPublicKey) { // some users might not have set up their key pair, so we can't share secrets with them yet
const publicKey = base64.parse(user.ecdhPublicKey);
Expand Down
10 changes: 6 additions & 4 deletions frontend/src/components/NavigationBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@

<script setup lang="ts">
import { Disclosure, DisclosureButton, DisclosurePanel, Menu, MenuButton, MenuItem, MenuItems } from '@headlessui/vue';
import { ArrowRightOnRectangleIcon, Bars3Icon, ListBulletIcon, UserIcon, WrenchIcon, XMarkIcon } from '@heroicons/vue/24/outline';
import { onMounted, ref } from 'vue';
import { ArrowRightStartOnRectangleIcon, Bars3Icon, ListBulletIcon, UserIcon, WrenchIcon, XMarkIcon } from '@heroicons/vue/24/outline';
import { FunctionalComponent, onMounted, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import auth from '../common/auth';
import { UserDto } from '../common/backend';
Expand All @@ -80,6 +80,8 @@ const navigation = [
{ name: 'nav.vaults', to: '/app/vaults' },
];

type ProfileDropdownItem = { icon: FunctionalComponent, name: string, to: string };

const profileDropdownSections = {
infoSection :
[
Expand All @@ -94,12 +96,12 @@ const profileDropdownSections = {

hubSection :
[
{ icon: ArrowRightOnRectangleIcon, name: 'nav.profile.signOut', to: '/app/logout' }
{ icon: ArrowRightStartOnRectangleIcon, name: 'nav.profile.signOut', to: '/app/logout' }
],

};

const profileDropdown = ref<any []>([]);
const profileDropdown = ref<ProfileDropdownItem[][]>([]);
const props = defineProps<{
me : UserDto
}>();
Expand Down
8 changes: 4 additions & 4 deletions frontend/src/components/SignUserKeysDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@
<div class="my-2">
<label for="fingerprint" class="block text-sm font-medium leading-6 text-gray-900">Fingerprint</label>
<div class="relative mt-2 rounded-md shadow-sm">
<input type="text" name="fingerprint" v-model="enteredFingerprint" :readonly="minVerificationLen === 0" @keyup="tryAutocomplete()" id="fingerprint" class="block w-full rounded-md border-0 py-1.5 pr-10 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-primary text-sm sm:leading-6" />
<input id="fingerprint" v-model="enteredFingerprint" type="text" name="fingerprint" :readonly="minVerificationLen === 0" class="block w-full rounded-md border-0 py-1.5 pr-10 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-inset focus:ring-primary text-sm sm:leading-6" @keyup="tryAutocomplete()" />
<div class="pointer-events-none absolute inset-y-0 right-0 flex items-center pr-3">
<CheckIcon class="h-5 w-5 text-primary" aria-hidden="true" v-if="fingerprintMatches"/>
<CheckIcon v-if="fingerprintMatches" class="h-5 w-5 text-primary" aria-hidden="true"/>
</div>
</div>
</div>
Expand Down Expand Up @@ -94,7 +94,7 @@ async function fetchData() {
expectedFingerprint.value = await wot.computeFingerprint(props.user);
minVerificationLen.value = (await backend.settings.get()).wotIdVerifyLen;
if (minVerificationLen.value === 0) {
enteredFingerprint.value = expectedFingerprint.value.replace(/.{8}/g, "$&" + " ").trim();
enteredFingerprint.value = expectedFingerprint.value.replace(/.{8}/g, '$&' + ' ').trim();
}
}

Expand All @@ -105,7 +105,7 @@ function show() {
async function tryAutocomplete() {
if (enteredFingerprint.value.length >= minVerificationLen.value) {
if (expectedFingerprint.value?.startsWith(enteredFingerprint.value)) {
enteredFingerprint.value = expectedFingerprint.value.replace(/.{8}/g, "$&" + " ").trim();
enteredFingerprint.value = expectedFingerprint.value.replace(/.{8}/g, '$&' + ' ').trim();
}
}
}
Expand Down
18 changes: 9 additions & 9 deletions frontend/src/components/TrustDetails.vue
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
</PopoverButton>

<transition enter-active-class="transition ease-out duration-100" enter-from-class="transform opacity-0 scale-95" enter-to-class="transform opacity-100 scale-100" leave-active-class="transition ease-in duration-75" leave-from-class="transform opacity-100 scale-100" leave-to-class="transform opacity-0 scale-95">
<PopoverPanel class="absolute right-0 z-10 mt-2 origin-top-right rounded-md bg-white p-4 shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none">
<p v-if="trustLevel === -1" class="text-sm mb-2">{{ t('trustDetails.trustLevel.untrusted') }}</p>
<p v-else-if="trustLevel === 0" class="text-sm mb-2">{{ t('trustDetails.trustLevel', [ n(1, 'percent')]) }}</p>
<p v-else-if="trustLevel > 0" class="text-sm mb-2">{{ t('trustDetails.trustLevel', [ n(1 / trustLevel, 'percent')]) }}</p>
<button v-if="trustLevel !== 0 && trustedUser.ecdhPublicKey && trustedUser.ecdsaPublicKey" @click="showSignUserKeysDialog()" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-primary text-base font-medium text-white hover:bg-primary-d1 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary sm:w-auto sm:text-sm">
{{ t('trustDetails.showSignDialogBtn') }}
</button>
<p v-if="!trustedUser.ecdhPublicKey || !trustedUser.ecdsaPublicKey" class="text-sm mb-2">{{ t('trustDetails.userNotSetUp') }}</p>
</PopoverPanel>
<PopoverPanel class="absolute right-0 z-10 mt-2 origin-top-right rounded-md bg-white p-4 shadow-2xl ring-1 ring-black ring-opacity-5 focus:outline-none">
<p v-if="trustLevel === -1" class="text-sm mb-2">{{ t('trustDetails.trustLevel.untrusted') }}</p>
<p v-else-if="trustLevel === 0" class="text-sm mb-2">{{ t('trustDetails.trustLevel', [ n(1, 'percent')]) }}</p>
<p v-else-if="trustLevel > 0" class="text-sm mb-2">{{ t('trustDetails.trustLevel', [ n(1 / trustLevel, 'percent')]) }}</p>
<button v-if="trustLevel !== 0 && trustedUser.ecdhPublicKey && trustedUser.ecdsaPublicKey" class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-primary text-base font-medium text-white hover:bg-primary-d1 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary sm:w-auto sm:text-sm" @click="showSignUserKeysDialog()">
{{ t('trustDetails.showSignDialogBtn') }}
</button>
<p v-if="!trustedUser.ecdhPublicKey || !trustedUser.ecdsaPublicKey" class="text-sm mb-2">{{ t('trustDetails.userNotSetUp') }}</p>
</PopoverPanel>
</transition>
</Popover>

Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/UserProfile.vue
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ const version = ref<VersionDto>();
const onFetchError = ref<Error | null>();

onMounted(async () => {
let cfg = config.get();
const cfg = config.get();
keycloakUserAccountURL.value = `${cfg.keycloakUrl}/realms/${cfg.keycloakRealm}/account`;
await fetchData();
});
Expand Down
Loading