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

✨ Webdav connector to the Twake Drive #643

Draft
wants to merge 33 commits into
base: main
Choose a base branch
from
Draft

Conversation

rumata-feathers
Copy link
Collaborator

@rumata-feathers rumata-feathers commented Aug 27, 2024

WebDAV implementation

Description

This is implementation the WebDAV protocol for our application using the nephele library.
Key components of this implementation include:

  • Creating new 'webdav' service
  • Custom adapter class to interface
  • Resource class wrapper for drive items
  • Device authorisation
  • Implementing locks for the drive items

Related Issue

#573

Motivation and Context

WebDAV protocol is needed for desktop synchronisation

How Has This Been Tested?

  1. Manual testing using curl requests to simulate various WebDAV operations
  2. Practical testing by mounting the WebDAV server as a network drive:
    • Tested on macOS Sonoma
    • Tested file creation, deletion, and modification through the mounted drive

Testing environment:

  • Native explorer
  • ACCOUNTS_TYPE=internal

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added the Signed-off-by statement at the end of my commit message.

@rumata-feathers rumata-feathers requested review from MontaGhanmy, shepilov and ericlinagora and removed request for MontaGhanmy and shepilov August 27, 2024 14:15
Comment on lines +37 to +38
@Column("timeout", "number")
timeout: number;
Copy link
Member

@chibenwa chibenwa Aug 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A duration without unit? at least in a comment...

Comment on lines 12 to 13
@Column("id", "string")
@Column("id", "uuid", { generator: "uuid" })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we migrate existing data?
Is changing type of a column allowed on a production system?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this, the special ORM doesn't do I think... but that table should be empty for the moment, this is the first actual use, we'll have to remember to test this before deploy

Comment on lines +16 to +17
@Column("password", "string", { generator: "uuid" })
password: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this random enough? For such a sensible use case I would make sure I use cryptography grade random with enough entropy.

Also: does it make sense to store a password in clear? What's the impact if it gets read?

If it is stored only for validation later shouldn't we use bcrypt and friends?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might need a clear text to support digest authentication (if realm is configurable).

The UUID should have enough entropy for crypto https://github.com/uuidjs/uuid

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rumata-feathers, could you please take a look at how password is encoded in the "user" entity, and do the same with the same salt and the same passwordEncode component

export const adapterServiceReady = initializeAdapterService();

export const getDriveExecutionContext = (url: URL): DriveExecutionContext => ({
//TODO[ASH] username will be actually a device, and we will need to get real user by device
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still relevant? Remove TODO?

Copy link
Member

@chibenwa chibenwa left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partial review.
Disclaimer: I do not know tdrive, I do not know dav.
Feel free to discard comments if irrelevant.
Also: no tests? I see complex logic all across the place and many way things can go bust. It would be invaluable to invest in HTTP layer tests exercising this brand new DAV extension.
(from my experience standards needs extensive tests)

@@ -116,7 +116,7 @@ export default class WebServerService extends TdriveService<WebServerAPI> implem
},
});
this.server.register(formbody);
this.server.register(corsPlugin, this.configuration.get<FastifyCorsOptions>("cors", {}));
// this.server.register(corsPlugin, this.configuration.get<FastifyCorsOptions>("cors", {}));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Am I reading this right ? Does this disable CORS site-wide ????

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it should be reconfigured only for the webdav uri

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm surprised webdav clients care at all about CORS

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had similar issues with JMAP

  • CORS do not makes sense for JMAP so native JMAP clients ignores them
  • But web based JMAP clients are bound to browser safety limitations hence CORS...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, but still I'd prefer if native clients indeed ignore to keep them everywhere, and if/when we do have a browser client to support, and if it is a problem, then we look into removing them for that

@@ -118,6 +119,9 @@ export class DriveFile {
@Type(() => String)
@Column("scope", "string")
scope: DriveScope;

@Column("locks", "encoded_json")
locks: DriveLock[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If DriveLocks are stored in that json field, why create the entity and db table and all that ?

@@ -1317,4 +1328,157 @@ export class DocumentsService {
throw new CrudException(`Not enough space: ${size}, ${leftQuota}.`, 403);
}
};

/**
* Returns the file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont think this kind of documentation really adds anything ("returns the file" it's not hard to see the return type). However it would be nice, also eg on exported things like DriveLock

};

/**
* Copies item to destination
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...

this.logger.error({ error: `${error}` }, "Failed to grant access to the drive item");
throw new CrudException("User does not have access to this item or its children", 401);
}
return Promise.resolve(driveFile);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for Promise.resolve in an async function

): Promise<void> => {
assert(id != null, "Cannot copy null");
const item = await this.get(id, context);
assert(item.item.parent_id != null, "Cannot copy root");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what if id is not found ? How do you return say a 404 ? What about the asserts, shouldn't a lot of them be 400/401 type errors ?

reply.send({ message: "Test route" });
});
fastify.all("webdav", (request, reply) => {
reply.send({ message: "Test route" });
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needed ?

fastify.all("webdav", (request, reply) => {
reply.send({ message: "Test route" });
});
console.debug(fastify.printRoutes());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.


return routes;
}
export const routes = eval("import('nephele').then(builder)");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd really prefer if we did this horrible eval hack in a single place and exported what we need from there, and documented the raison-d'etre

tdrive/backend/node/src/utils/files.ts Show resolved Hide resolved
@@ -16,7 +16,7 @@
"src/types/*"
],
"src/*": ["src/*"]
}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

technically that's not valid json, seems benign, but also, why in this pr ?

@shepilov shepilov changed the title Webdav express ✨ Webdav connector to the Twake Drive Aug 28, 2024
@ericlinagora ericlinagora marked this pull request as draft September 2, 2024 22:21
@@ -76,14 +75,6 @@ async function builder(nephele: any) {
});
fastify.use(`${webdavUrl}`, webdavMiddleware);
});
fastify.all("webdav/*", (request, reply) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apparently this chunk is vital, without it, we get 404s only in the webdav stuff....

Copy link
Member

@shepilov shepilov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!

@@ -62,7 +62,7 @@ jobs:
- name: e2e-mongo-s3-test
run: |
cd tdrive
docker compose -f docker-compose.tests.yml run --rm -e NODE_OPTIONS=--unhandled-rejections=warn -e SEARCH_DRIVER=mongodb -e DB_DRIVER=mongodb -e PUBSUB_TYPE=local node npm run test:all
docker compose -f docker-compose.tests.yml run --rm -e "NODE_OPTIONS=--unhandled-rejections=warn --experimental-vm-modules" -e SEARCH_DRIVER=mongodb -e DB_DRIVER=mongodb -e PUBSUB_TYPE=local node npm run test:all
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still needed after the eval evil hack ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants