{ "info": { "author": "Mike Kazantsev", "author_email": "mk.fraggod@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 4 - Beta", "Environment :: Plugins", "Environment :: Web Environment", "Framework :: Django", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "Intended Audience :: System Administrators", "License :: OSI Approved", "Natural Language :: English", "Operating System :: POSIX", "Operating System :: Unix", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 2 :: Only", "Topic :: Database :: Front-Ends", "Topic :: Internet :: WWW/HTTP :: WSGI :: Application", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": "django-remotestorage\n--------------------\n\n`Unhosted `_\n`remoteStorage `_ server implementation\n\nThis app is a server (storage) implementation for an earlier\nremoteStorage API version, specified here:\n\n::\n\n http://www.w3.org/community/unhosted/wiki/RemoteStorage-2011.10\n\nSome parts of it (especially webfinger, oauth2, since I've used newer\nspecs that were available at the time) *might* be compatible with the\ncurrent API:\n\n::\n\n https://tools.ietf.org/id/draft-dejong-remotestorage-00.txt\n\nBut since remoteStorage.js 0.7.0 for experimental API was still under\nheavy development at the time, I haven't tested whether it works with\ncurrent implementation.\n\nPackage (and django app) was called django-unhosted in the past and was\neventually renamed. If you're using django-unhosted package, please read\nthe `notes on\nmigration `_\nunder\n`Installation `_\nsection.\n\nremoteStorage\n-------------\n\nIdea is that you can have storage account (with whatever policies and\nauthentication) on host1 and some webapp (say, some visual editor, think\nMS Word) on host2.\n\nTo edit document in a webapp, generally host2 would have to implement\nsome sort of user registration, storage (like docs.google.com) for\nedited docs, etc.\n\nWith remoteStorage, this storage don't have to be on host2, so you don't\nhave to implement some complex policies and authenticated storage there\nto launch a full-featured webapp - it can open and save docs to any\nremote host which supports the protocol (which is basically GET/PUT from\nWebDAV with OAuth2 on top).\n\nhost1 can be your VPS, client machine itself (especially easy with\ndirect IPv6, or IPv4 provided via some service like\n`pagekite `_), some reliable cloud provider or\nwhatever.\n\nTo fully understand how it all works, I recommend looking at OAuth2,\nWebDAV, CORS and Webfinger, which are basically all the technologies\nused to implement the protocol.\n\nThis django app fully implements web-facing storage for host1, complete\nwith user registration forms (optional, users can be added by other\ndjango apps or via django admin interface otherwise), client access\nmanagement interfaces and a simple demo client.\n\nSecurity\n--------\n\nSince applicaton is a public-internet-facing interface to your (possibly\nimportant) data and I'm in no way security expert or specialist, I\nrecommend to pentest or validate the code before storing any sensitive\ndata in it.\n\nData loss or corruption is much easier to prevent (and backups go a long\nway here, btw) than security exploits, so, again, please look at the\ncode yourself and find issues there which I have a blind spot (not to\nmention lack of skills) for, thus won't be able to find on my own.\n\nExample of *obvious* (to an outsider analysis) security flaws in another\nstorage-server implementation `can be found\nhere `_,\nlearn the lession there.\n\nInstallation\n------------\n\nRequirements\n~~~~~~~~~~~~\n\n- `Python 2.7 (not 3.X) `_\n\n- `Django `_\n- `Django OAuth 2.0 Server App\n (oauth2app) `_\n- (optional, recommended) `South `_ - for\n automated database schema updates\n\noauth2app is `not on\nPyPI `_ at the moment, but\npip can install it from github directly.\n\nVarious interfaces of the app use some external resources, like `Twitter\nBootstrap `_ CSS file (served from\nbootstrapcdn.com) and\n`remoteStorage.js `_,\nwhich can be served - and **should be**, if you're using https for\ninterfaces - from local URLs, if available in STATIC\\_ROOT. See\n\"Customization / Interfaces\" for details.\n\nMigration from django-unhosted\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nPackage was called django-unhosted in the past, but it was decided to\nrename it before it was way too late.\n\nUnfortunately, there's no easy way to rename django app and python\npackage, especially if it's undesirable to keep older package names\naround for eternity, so some manual steps have to be taken in order to\nmigrate to the new (django-remotestorage) app/package.\n\n- Uninstall django-unhosted python package (either through\n ``pip uninstall django-unhosted``, OS tools, or remove module\n path manually).\n\n- Rename all database tables with \"django\\_unhosted\" in name to be\n starting with \"django\\_remotestorage\" instead. Lots of easy-to-use\n GUI tools (such as `pgadmin `_,\n `phpPgAdmin `_,\n `phpMyAdmin `_,\n `phpSQLiteAdmin `_, etc) or\n native CLI interfaces (``sqlite3 /path/to/db.sqlite``, ``psql``,\n ``mysql``, etc) can be used for that.\n\n- Update settings.py and urlconf to import stuff from\n \"django\\_remotestorage\" module instead of \"django\\_unhosted\". Replace\n all \"UNHOSTED\\_\" in variable names to \"REMOTESTORAGE\\_\", if used in\n settings.py.\n\n- If you have a custom urlconf and/or templates, replace references to\n \"unhosted\" namespace with \"remotestorage\".\n\nIt should fairly straightforward, but feel free to open Issue or\n`contact\ndevelopers `_\nif the described process doesn't work for you.\n\nDeployment / configuration\n~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nDjango apps are deployed as a part of \"django project\", which is - at\nit's minimum - just a few `configuration\nfiles `_,\nspecifying which database to use, and which apps should handle which\nURLs.\n\nTL;DR\n'''''\n\nSimple installation/setup from scratch may look like this.\n\nInstall the app itself (or not, it can be just checked-out into a\nproject dir):\n\n::\n\n pip install django-remotestorage\n\n...or, to install directly from git master branch:\n\n::\n\n pip install 'git+https://github.com/RemoteStorage/django-remotestorage.git#egg=django-remotestorage'\n\n...or you can do it manually:\n\n::\n\n git clone https://github.com/RemoteStorage/django-remotestorage.git\n cd django-remotestorage\n python setup.py install\n pip install -r requirements.txt # or download/install each by hand as well\n\n\"pip\" tool, mentioned above, should usually come with OS of choice,\notherwise see `pip installation\ndocs `_. Don't\nuse \"easy\\_install\" for anything except installing the pip itself.\n\nInstall oauth2app in a similar fashion:\n\n::\n\n pip install 'git+https://github.com/hiidef/oauth2app.git#egg=oauth2app'\n\nThen create and configure a django project:\n\n::\n\n cd\n django-admin.py startproject myproject\n cd myproject\n\n # Update settings.py (sqlite3 is used as db here) and urls.py\n sed -i \\\n -e 's/'\\''ENGINE'\\''.*/\"ENGINE\": \"django.db.backends.sqlite3\",/' \\\n -e 's/'\\''NAME'\\''.*/\"NAME\": \"db.sqlite\",/' \\\n -e 's/STATIC_ROOT *=/STATIC_ROOT=\"./static\"/' \\\n myproject/settings.py\n echo -e >>myproject/settings.py \\\n 'from django_remotestorage.settings_base import update_settings' \\\n '\\nupdate_settings(__name__)'\n sed -i \\\n -e '1afrom django_remotestorage.urls import remotestorage_patterns' \\\n -e 's/# Examples:.*/(\"\", include(remotestorage_patterns)),\\n\\n\\0/' \\\n myproject/urls.py\n\n # Create database schema and link app static files to STATIC_ROOT\n ./manage.py syncdb --noinput\n ./manage.py migrate django_remotestorage\n ./manage.py collectstatic --noinput --link\n\n # Run simple dev server\n ./manage.py runserver\n\n(since webfinger protocol **requires** some sort of XRD authentication,\nlike https, it *won't work* properly on such a simple setup)\n\nMore detailed explaination of configuration process follows.\n\nDjango project configuration\n''''''''''''''''''''''''''''\n\nMain idea is that two config files (in django project) need to be\nupdated - settings.py and urls.py.\n\nThere are several ways to update django settings.py to use the app:\n\n- If it's the only app in a django project and there's no custom\n settings.py already, options from\n `django\\_remotestorage.settings\\_base `_\n module can be imported into it directly.\n\n To do that, add the following lines to the *end* of\n \"{your\\_app\\_name}/settings.py\" (or wherever\n `DJANGO\\_SETTINGS\\_MODULE `_\n is used) file:\n\n ::\n\n from django_remotestorage.settings_base import *\n\n That will import all the options there (bare minimum that has to be\n changed) over those defined above in the original file.\n\n Note that list of overidden options include INSTALLED\\_APPS,\n MIDDLEWARE\\_CLASSES and such, which are not only often customized,\n but are usually specific to the django version installed, so you may\n alternatively insert that import line at the *beginning* of the\n settings.py, so everything defined after it will override the\n imported options.\n\n- If there's already some custom settings.py file available, there's\n django\\_remotestorage.settings\\_base.update\\_settings helper function\n available to update configuration without blindly overriding any\n options.\n\n It can be used at the end of settings.py file like this:\n\n ::\n\n from django_remotestorage.settings_base import update_settings\n update_settings(__name__)\n\n Full list of changes it'll make can be found in \"updates\" dict at the\n beginning of\n `django\\_remotestorage.settings\\_base `_\n module.\n\n \"update\\_settings\" function also takes an optional \"only\" and\n \"ignore\" keywords (expecting an iterable of option names), which can\n be used to control which parameters should be updated or explicitly\n left untouched.\n\n This should be more safe, flexible and future-proof way of merging\n necessary option updates with existing (site-specific) configuration.\n\n- Update the file by hand.\n\n Default values for the most settings can be found in `django\n documentation `_.\n\n For the class-listing type options, duplicate values may be omitted.\n Note that order of MIDDLEWARE\\_CLASSES is significant.\n\n ::\n\n OAUTH2_CLIENT_KEY_LENGTH = 1024\n OAUTH2_SCOPE_LENGTH = 2048\n\n TEMPLATE_CONTEXT_PROCESSORS = (\n ...whatever is already there...\n 'django.core.context_processors.csrf',\n 'django.core.context_processors.request',\n 'django.contrib.messages.context_processors.messages',\n 'django_remotestorage.utils.external_resources_context',\n )\n\n TEMPLATE_LOADERS = (\n ...whatever is already there...\n 'django_remotestorage.apps.webfinger.xrd_gen.Loader',\n )\n\n MIDDLEWARE_CLASSES = (\n ...whatever is already there...\n \n ...whatever is already there, except for ConditionalGet / FetchFromCache...\n 'django.contrib.messages.middleware.MessageMiddleware',\n ...ConditionalGetMiddleware and FetchFromCacheMiddleware (and such), if used...\n )\n\n INSTALLED_APPS = (\n ...whatever is already there...\n 'django.contrib.messages',\n 'django_remotestorage',\n 'oauth2app',\n 'south',\n )\n\n \"south\" should be omitted from INSTALLED\\_APPS, if not used.\n\nIn any case, if you've just created django project (with\n\"django-admin.py startproject\" or whatever), make sure to look through\nit's settings.py file and edit at least DATABASES, MEDIA\\_\\* and\nSTATIC\\_\\* options. You might also want to set other (optonal) settings\nthere - TIME\\_ZONE, ADMINS, LOGGING, etc.\n\nAs for urls.py, just add the following line to url patterns (importing\nremotestorage\\_patterns from django\\_remotestorage.urls module\nbeforehand):\n\n::\n\n ('', include(remotestorage_patterns)),\n\nSo it'd look like this:\n\n::\n\n ...\n from django_remotestorage.urls import remotestorage_patterns\n ...\n urlpatterns = patterns('',\n ('', include(remotestorage_patterns)),\n ...\n\nThat will add all the app urls to the root-path (for the complete list\nof these paths, see `the module\ncode `_).\nTo selectively disable some of the components, see \"Customization\"\nsection.\n\nDatabase schema\n'''''''''''''''\n\nThen the usual drill is to create the necessary database schema for the\napp (if you get \"Settings cannot be imported\" error, make sure you run\nthat from the same path as \"settings.py\" file):\n\n::\n\n django-admin.py syncdb\n\nIf `South app `_ is installed (and specified\nin the INSTALLED\\_APPS), you should also use it's migrations to create\ntables for which they are available:\n\n::\n\n django-admin.py migrate django_remotestorage\n\nThat command can (and should) also be run after django-remotestorage app\nupdates to apply any possible changes to db schema.\n\nRunning\n^^^^^^^\n\nPretty much anything that supports\n`WSGI `_\nprotocol can be used with django - there's nothing app-specific here,\njust plain django, which is (usually) used as a backend with some httpd\nvia wsgi.\n\nSee django docs on `deployment\nprocess `_ for\ngeneric instructions.\n\nCustomization\n-------------\n\nComponents\n~~~~~~~~~~\n\nThe app consists of several independent components (sub-apps, bound to\nurl paths via\n`django\\_remotestorage.urls `_):\n\n- Webfinger (name: webfinger, URL:\n {include\\_prefix}/.well-known/host-meta, {include\\_prefix}/webfinger;\n see\n `django\\_remotestorage.apps.webfinger.urls `_,\n there are similar urlconf-files for other subapps).\n\n- OAuth2 (name: oauth2, URL: {include\\_prefix}/oauth2).\n\n- Storage API (name: api, URL: {include\\_prefix}/api).\n\n- Account/client management (name: \"account\", URL:\n {include\\_prefix}/account). Can also be enabled partially with the\n following names: \"account\\_auth\" (login/logout forms/links),\n \"account\\_auth\\_management\" (signup form),\n \"account\\_client\\_management\" (client/app access management interface\n for logged-in users). \"account\" is an alias for all of these\n interfaces.\n\n- Demo client (name: demo, URL: {include\\_prefix}/)\n\nSome components provide links to each other (for example, webfinger\nprovides links to OAuth2 and API in served XRD/JSON data), resolved as\n\"remotestorage:{app}:{view\\_name}\", so you can rebind these apps to any\nURLs, as long as you provide the same namespace/view\\_name for `django\n\"reverse()\"\nfunction `_\nand \"url\" template tags.\n\nWhen including \"django\\_remotestorage.urls.remotestorage\\_patterns\"\ndirectly (not the urlconfs from individual components),\n\"REMOTESTORAGE\\_COMPONENTS\" settings.py option can be set to an iterable\nof components which should be enabled, for example:\n\n::\n\n REMOTESTORAGE_COMPONENTS = 'webfinger', 'oauth2', 'api'\n\n...will enable just Storage API, OAuth2 and Webfinger subapps - bare\nminimum for functional remoteStorage node. Unless some other means to\nauthenticate django user (like\n`django.contrib.auth.views.login `_\nor django.contrib.admin) are enabled, it might also be necessary to\nenable \"account\\_auth\" interface to pass OAuth2 authorization.\n\nIf \"account\" (or it's parts) and \"demo\" apps are omitted from urlconf\nentirely (if not needed), there won't be any links to them in OAuth2\naccess confirmation interface. Their interface pages and functionality\nwon't be accessible.\n\n\"api\" and \"oauth2\" sub-apps are not linked to any other components\neither, so may be used separately from others and from each other as\nwell (e.g. if authorization server and storage are on a different\nhosts), but they must share a database in order for api to be able to\nvalidate auth tokens.\n\nOAuth2\n~~~~~~\n\nIt's highly recommended to raise database field lengths (using\n`oauth2app\nsettings `_) *before*\nrunning syncdb for the first time:\n\n- OAUTH2\\_CLIENT\\_KEY\\_LENGTH = 1024 (default: 30)\n- OAUTH2\\_SCOPE\\_LENGTH = 2048 (default: 255)\n\nSee \"Known Issues / OAuth2\" section for more detailed explaination on\nwhy it should be done.\n\nAnother important tunable is OAUTH2\\_ACCESS\\_TOKEN\\_EXPIRATION (default:\n3600 = 1 hour), which - at least with remoteStorage.js 0.6.9 (\"stable\"\nat the moment of writing) - essentially sets a maximal interval between\nthe need to visit OAuth2 interface and get new access token, because\nremoteStorage.js doesn't seem to be able to refresh these.\n\nWebfinger\n~~~~~~~~~\n\nIf\n`webfinger `_\nand `host-meta `_\nrequests for the domain should carry more data than just for\nremoteStorage, they can be extended either by replacing webfinger app\nentirely or adding custom templates for it.\n\nWebfinger app is using \"webfinger/host\\_meta.{xml,json}\" and\n\"webfinger/webfinger.{xml,json}\" templates, provided by\ndjango\\_remotestorage.apps.webfinger.xrd\\_gen.Loader or generated\ndynamically (in case of json, if template provide can't be found).\n\nSee example xml templates in\n`django\\_remotestorage/templates/webfinger/{host\\_meta,webfinger}.xml.example `_.\n\nStorage / WebDAV\n~~~~~~~~~~~~~~~~\n\nProvided remoteStorage is backed by (configurable) `Django Storage\nAPI `_.\n\nBy default,\n`DEFAULT\\_FILE\\_STORAGE `_\nstorage class is used. Different storage class can be specified by\n\"REMOTESTORAGE\\_DAV\\_STORAGE\" parameter (passed to\n`get\\_storage\\_class `_).\n\nExamples of Storage API implementation might include:\n\n- `django-storages `_\n (S3, CouchDB, SQL, FTP, MongoDB, CloudFiles, etc)\n- `django-dropbox `_\n (Dropbox)\n- `django-riak-engine `_\n (Riak)\n- `django-tahoestorage `_\n (Tahoe-LAFS)\n\nBut basically there's a client for pretty much any data storage\ntechnology - just google it, install and set REMOTESTORAGE\\_DAV\\_STORAGE\n(or DEFAULT\\_FILE\\_STORAGE) to it.\n\nDefault Storage (FileStorage) parameters can be configured with\nMEDIA\\_URL and MEDIA\\_ROOT\n`settings `_, see\n`\"Managing\nfiles\" `_ django\ndocs section for details.\n\nThere are also some optimization parameters:\n\n- REMOTESTORAGE\\_DAV\\_SENDFILE (bool, default: False)\n\n Pass Storage.path (if supported by backend) to httpd frontend via\n \"X-Sendfile\" header instead of the actual contents upon request, so\n that response can be served by frontend daemon directly without\n backend app involved.\n\n- REMOTESTORAGE\\_DAV\\_ACCEL (string, default: None)\n\n Return empty HttpResponse with \"X-Accel-Redirect\" header set to\n specified prefix (can be an empty string) plus the requested path, so\n the actual response can be served by `apache\n mod\\_accel `_.\n\n- REMOTESTORAGE\\_DAV\\_REDIRECT (bool, default: False)\n\n Return redirect to MEDIA\\_URL (produced by Storage.url method). Used\n only if MEDIA\\_URL is set to non-empty string.\n\n Serve these urls only after checking oauth2app-generated bearer\n tokens in http \"Authorization\" header either with django (or custom\n python code) or some smart httpd.\n\n **Do not** configure httpd to serve paths from MEDIA\\_URL without\n authorization, because everyone will be able to bypass OAuth2 and\n gain access to anything in remoteStorage just by guessing file paths\n or getting/reusing them from js, which is really easy to exploit.\n\nInterfaces\n~~~~~~~~~~\n\nMostly usual drill - put your own templates to loaders, specified in\nsettings.py.\n\nExternal resources that are served on these pages can be put to\nSTATIC\\_ROOT to be served by local httpd instead. See\n`django\\_remotestorage.utils.external\\_resources\\_context `_\ncontext processor for details.\n\nTake special care to make resources local if you serve these interfaces\nover https - there's just no security gain if MitM can place any\njavascript (loaded over plain http) to a page.\n\nNote that any/all of the UIs can be disabled, if they're not needed,\njust use REMOTESTORAGE\\_COMPONENTS option (described in \"Components\"\nsection) or don't include them in the urlconf, cherry-picking whichever\nones are actually needed.\n\nOne common case of customization is the need to put whole app into some\nsubpath (\"/remotestorage\" in the example) can be addressed by putting\nthis into the project's root urls.py:\n\n::\n\n from django.conf.urls import patterns, include, url\n\n from django_remotestorage.apps.webfinger.urls import host_meta_patterns\n from django_remotestorage.urls import remotestorage_patterns\n\n urlpatterns = patterns('',\n url(r'', include(host_meta_patterns)),\n url(r'^remotestorage/', include(remotestorage_patterns)),\n )\n\nThat way, demo client will be available at \"/remotestorage\" url and all\nthe links will include that prefix (for example authorization link from\nwebfinger will point to \"/remotestorage/oauth2/authorize\").\n\nMake sure, however, that host\\_meta view of webfinger app is `available\nat a well-known\nurl `_\n\"/.well-known/host-meta\", hence the \"host\\_meta\\_patterns\" special-case\nlink from root.\n\nCommands\n--------\n\naccess\\_token\\_cleanup [options] [ username ... ]\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\nRemove expired OAuth access tokens (just for username(s), if specified)\nfrom the database.\n\nCan be occasionally run from cron (use --verbosity=0 to supress activity\nreports) to keep token number from growing indefinitely, removing\nnon-refreshed or about-to-expire (with negative --grace-period) ones.\n\nUsage example:\n\n::\n\n % ./manage.py access_token_cleanup -v2 -n -t 3600 test\n Removing token: id=1, user=test, client_name=localhost, expired=2012-07-31 03:24:30+06:00.\n 1 access token(s) removed.\n\nKnown issues\n------------\n\nThese are implementation-related issues, not the issues with the\nprotocols themselves (which doesn't imply there's none of the latter,\njust that it's not a place for them).\n\nWebfinger\n~~~~~~~~~\n\n- No easy support for `signed\n XRD `_\n at the moment. Signed *static* xml \"templates\" (or just files, served\n from httpd) can be used as a workaround if TLS is not an option.\n\nOAuth2\n~~~~~~\n\n- Stored object path (think \"public/myphoto.jpg\") is used as OAuth2\n \"scope\" by remoteStorage. oauth2app basically keeps a single table of\n these (treating them as a finite up-front set of capabilities).\n\n Problems here:\n\n - oauth2app stores \"scope\" as a 255-char key, while paths /\n collection\\_names can potentially be longer. Upstream `pull\n request `_ to specify\n field length was merged (as of 19.07.2012), so use any newer\n version with the large-enough OAUTH2\\_SCOPE\\_LENGTH parameter in\n settings.py (it `doesn't really affect\n performance `_\n of modern databases, just making your life a bit harder).\n\n - Currently, oauth2app checks existance of AccessRange (scope)\n models as they are specified in the request, even though access to\n some of them might not be authorized by user, requiring temporary\n creation of this clutter. Upstream pull request:\n https://github.com/hiidef/oauth2app/pull/32\n\n - There's some extra code/db overhead involved in maintaining the\n (pointless in this case) table.\n\n- remoteStorage.js 0.6.9 (\"stable\" version at the moment) has a `known\n issue `_\n of passing legacy \"path1,path2\" as a \"scope\", further complicating\n things for oauth2app (which would think that it's a single\n capability, as per spec) if several paths are passed.\n\n Workaround used is to detect the old format by lack of \":rw\" suffixes\n and update \"scope\" in the address by issuing a redirect.\n\n Note that since paths may contain commas, \"path1,path2\" can be\n ambiguous (because of this issue) and can be treated either as\n \"path1:rw\" and \"path2:rw\" or \"path1,path2:rw\". Current implementation\n chooses the former interpretation if there's no colon-delimeted\n suffix.\n\n- remoteStorage.js 0.6.9 (\"stable\" version at the moment) uses hostname\n of the app site as OAuth2 client\\_id, which, in oauth2app corresponds\n to the \"key\" field, which is just 32-chars long by default, which\n might not be enough for some hostnames, but can (and *should*!) be\n configured by OAUTH2\\_CLIENT\\_KEY\\_LENGTH parameter in django\n project's settings.py. Remember to do that *before* syncdb, or update\n the table column later.\n\n Possible workaround might be to use hashes as the client\\_id's\n internally and redirect remoteStorage requests with\n \"client\\_id=hostname.com\" to something like\n \"client\\_id=sha1:bbc21f0ccb5dfbf81f5043d78aa\".\n\n I can't see why client\\_id should be random or non-meaningful at the\n moment, if there's a reason for that, please report an issue, some\n automatic migration to hashes can probably be deployed at any time.\n\n- oauth2app is `not on\n PyPI `_ at the moment,\n but pip can install it from github directly.\n\nWebDAV\n~~~~~~\n\n- CSRF middleware\n (`django.middleware.csrf.CsrfViewMiddleware `_)\n must be disabled, because remoteStorage.js doesn't pass django csrf\n tokens along with PUT (and similar) requests. It's selectively\n enabled via decorator for app forms though.\n\n- Data is currently stored in the Django Storage, while path metadata\n is stored through the Django Database API, which introduces two\n points of failure (and the possibility of sync loss between the two),\n because one data is useless without the other.\n\n There don't seem to be any easy way around it - storing path data in\n Storage keys won't work with any driver, pushing that to the content\n won't work when this content will be served by anything but python\n (say, httpd) and storing files in a db only works well for relatively\n small files.\n\n So make sure to backup db as well as the actual storage, or write\n some storage-specific kludge to store metadata there as well. Example\n would be to add a hook to `post-save django\n signal `_,\n which would get storage path from StorageObject.data.name and store\n some \"{name}.meta\" file alongside with serialized model data.\n\nTODO\n----\n\n- Client (app, requesting access) deception - returning fake\n \"authorized scopes\" to it, but storing them somewhere to deny the\n actual access or provide random garbage instead.\n\n Idea is to prevent situation, common on twitter and android\n platforms, when apps always ask for everything and user is presented\n with \"all or nothing\" choice.\n\n- Add ability to inspect stored/accessed resources to the client\n management interface.\n\nContacts / Support\n------------------\n\nFeel free to drop by to #unhosted or #remotestorage channels on\n`freenode IRC `_, you can always find authors and\npeople (developers included) willing to help understand, setup and\nresolve any issues there.\n\nMailing lists, twitter and other channels of indirect communication can\nalso be found on `Unhosted movement site `_.\n\nAnd of course, open Issues for `github\nrepository `_.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "https://github.com/RemoteStorage/django-remotestorage", "keywords": "django unhosted app remoteStorage server cloud silo storage oauth webfinger xrd read-write-web webdav", "license": "WTFPL", "maintainer": null, "maintainer_email": null, "name": "django-remotestorage", "package_url": "https://pypi.org/project/django-remotestorage/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/django-remotestorage/", "project_urls": { "Download": "UNKNOWN", "Homepage": "https://github.com/RemoteStorage/django-remotestorage" }, "release_url": "https://pypi.org/project/django-remotestorage/13.08.0/", "requires_dist": null, "requires_python": null, "summary": "Unhosted remoteStorage server app for django", "version": "13.08.0" }, "last_serial": 836474, "releases": { "12.09.0": [ { "comment_text": "", "digests": { "md5": "d939f43ef75a0ca3f0f9bd4a3d05366f", "sha256": "b9257bca3df716b78c51d6a716615f861763dc470735325b410041640c7691a2" }, "downloads": -1, "filename": "django-remotestorage-12.09.0.tar.gz", "has_sig": true, "md5_digest": "d939f43ef75a0ca3f0f9bd4a3d05366f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 51980, "upload_time": "2012-09-15T10:36:14", "url": "https://files.pythonhosted.org/packages/73/3f/d5e496405819a8865864e2aabe7c4f863e4af813ea7672b1d372d8b4f94a/django-remotestorage-12.09.0.tar.gz" } ], "12.09.1": [ { "comment_text": "", "digests": { "md5": "d013943d2a983b22fecb28bc6c4ea621", "sha256": "93a0fb005db73dde7b351934a20d246f86e61b8a180be7074c3d4469cf37f473" }, "downloads": -1, "filename": "django-remotestorage-12.09.1.tar.gz", "has_sig": true, "md5_digest": "d013943d2a983b22fecb28bc6c4ea621", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 56988, "upload_time": "2012-09-15T11:05:57", "url": "https://files.pythonhosted.org/packages/a1/8d/f0a620d89d78eca64e8ddc67bcdf05763ed1f9b820cd00172dd0f53ff825/django-remotestorage-12.09.1.tar.gz" } ], "12.09.3": [ { "comment_text": "", "digests": { "md5": "26760c97a52a9322c3b547ae5bd56f7f", "sha256": "a9843eb4e3e25efad5a55dd9a234b222e3570d5bc0518d96da21954ea53f4930" }, "downloads": -1, "filename": "django-remotestorage-12.09.3.tar.gz", "has_sig": true, "md5_digest": "26760c97a52a9322c3b547ae5bd56f7f", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 56970, "upload_time": "2012-09-15T11:24:53", "url": "https://files.pythonhosted.org/packages/18/1d/92651d297ed9c44d94515c023b55fc108570d1f1aa455517efe6a14f149b/django-remotestorage-12.09.3.tar.gz" } ], "12.09.4": [ { "comment_text": "", "digests": { "md5": "d4e7689a918104ac6684e0aa7ef8ae13", "sha256": "74baf6066189b8b07059446ec812812c75f2d4f1ef1e5526cd06f6b9cf85325a" }, "downloads": -1, "filename": "django-remotestorage-12.09.4.tar.gz", "has_sig": true, "md5_digest": "d4e7689a918104ac6684e0aa7ef8ae13", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 56966, "upload_time": "2012-09-22T11:45:56", "url": "https://files.pythonhosted.org/packages/2a/53/ccf9effe6e8f072592a0e3ca16e49b4f1ae458ba23ced62c1bbf642c03f1/django-remotestorage-12.09.4.tar.gz" } ], "13.08.0": [ { "comment_text": "", "digests": { "md5": "25837a6c3bab62c2188f1815bdf82683", "sha256": "f507cb59560bd613cc4e8abf4ace03444193c8d04f66a19e92bef7c90c91c043" }, "downloads": -1, "filename": "django-remotestorage-13.08.0.tar.gz", "has_sig": true, "md5_digest": "25837a6c3bab62c2188f1815bdf82683", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 56990, "upload_time": "2013-08-09T14:06:27", "url": "https://files.pythonhosted.org/packages/08/54/6071f495d2a1db8b2a46eed4c158eb6172eb7091d6f4e945eb0a8083cb0f/django-remotestorage-13.08.0.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "25837a6c3bab62c2188f1815bdf82683", "sha256": "f507cb59560bd613cc4e8abf4ace03444193c8d04f66a19e92bef7c90c91c043" }, "downloads": -1, "filename": "django-remotestorage-13.08.0.tar.gz", "has_sig": true, "md5_digest": "25837a6c3bab62c2188f1815bdf82683", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 56990, "upload_time": "2013-08-09T14:06:27", "url": "https://files.pythonhosted.org/packages/08/54/6071f495d2a1db8b2a46eed4c158eb6172eb7091d6f4e945eb0a8083cb0f/django-remotestorage-13.08.0.tar.gz" } ] }