This is a Girder plugin that adds user home directory capability accessible through WebDAV. It uses WsgiDAV. It also allows access to tale directories.
The WebDAV URL for home directories is /homes/<username>
. Example:
fusedav https://localhost:8080/homes/wtuser ~/wthome
Similarly, the URL for tales is /tales/<taleId>
.
Specifies where the home directory data is stored. This should be a filesystem
path accessible by the Girder server. If this path does not exist, it will be created. User home directories are stored in specific user directories and are named <wt.homedir.root>/<username>
.
Like wt.homedir.root
except for tale directories.
Updating the root directories does not copy data. Since girder maintains duplicate filesystem data, such an update without a manual copy of the data from the old root to the new one may result in inconsistencies between what girder sees and what the WebDAV server sees.
The plugin is configured to only accept basic authentication. It should only be used over HTTPS. The other choice would have been digest authentication. The reason for avoiding that is that it either requires storing plaintext (or obfuscated) passwords or a hash (HA1 - see Digest Access Authentication) that could potentially be susceptible to dictionary attacks. So complexity + not such a good idea = use the other thing.
Authentication itself is done using either the OAUTH token, a user-specified password, or an automatically generated password. If the Girder OAUTH token is used, the password must be constructed by pre-pending token:
to the token value. Example:
fusedav -u wtuser \
-p token:jPuIdOjh9A1Q1Bhshxop7yuhToKSM0WgdVZxGQqHjUTLEeHQ65qzVZ9faBW6WpEz \
https://localhost:8080/homes/wtuser ~/wthome
Users can either set a password or request a random password using /#homedir/password
. Only one password can be active at a time and generating a random password or setting a new password overwrited previous passwords. If user-set or generated passwords are used, they should be specified directly when authenticating:
fusedav -u wtuser -p p7yuhToK \
https://localhost:8080/homes/wtuser ~/wthome
There is some throttling enabled. More than 5 failed authentication requests for one user in one minute will trigger a lockout for the remainig time in that minute (or at least that's the intention of the code).
At this point the API is limited to password management.
PUT /homedirpass/set
The user for which the WebDAV password is being set must be the current user. The password must be sent as form data in a string.
GET /homedirpass/generate
There are no parameters. The generated password is returned as a JSON object in the form {'password': <password>}
In order to allow filesystem browsing through existing infrastructure (i.e., Girder), the home directory plugin maintains a "shadow" filesystem structure in Girder. The Girder filesystem structure is synchronized with the WebDAV version. Only metadata is stored in Girder and data is only maintained in WebDAV accessible directories. The synchronization between Girder and WebDAV is a two way process.
In the WebDAV -> Girder direction, hooks into the WebDAV implementation are used to send relevant requests to Girder. Such requests include file/folder create/copy/move/delete. File data is handled using a custom assetstore and assetstore adapter which simply get the file data from the home directory backing storage, which stores files in a standard filesystem.
The Girder -> WebDAV synchronization is handled through events generated by Girder as well as assetstore adapter operations. Specifically, the mapping between operations and mechanisms handling them is:
Operation | Handling |
---|---|
create file | AssetstoreAdapter.finalizeUpload |
delete file | AssetstoreAdapter.deleteFile |
copy file | model.item.save event |
move file | model.item.save event |
rename file | model.item.save event |
create folder | model.folder.save event |
delete folder | model.folder.remove event |
copy folder | model.folder.save event |
move folder | model.folder.save event |
rename folder | model.folder.save event |
Since Girder does not generate distinct events for the various operations, the code has to infer the type of operation from data associated with events. In particular, the following algorithm is used to distinguish between the various folder operations using a folder
object obtained from a model.folder.save
event:
if '_id' in folder:
# the object has an ID, so it is not a new folder
# find the object that's already stored in the Girder database
storedFolder = Girder.findFolder(folder['_id'])
if storedFolder['name'] != folder['name']:
DAV.renameFolder(...)
if storedFolder['parentId'] != folder['parentId']:
DAV.moveFolder(...)
else: # '_id' not in folder
DAV.createFolder(...)
There is no special handling needed for file and folder copy operations since Girder handles them recursively through relevant create file/folder operations.