Skip to content

Export to Google Drive functionality

Antonio Monje edited this page Mar 2, 2016 · 4 revisions

Table of Contents

Definition

The main goal for this task, is develop a feature to publish an eXe document as a public web site using the user's Google Drive account.

Right now, this can be done exporting the eXe document as full web-site (Export > Web Site > Self-contained Folder), uploading the generated folder to the users GDrive account and then configuring the uploaded folder as public. This feature would save the user the last two steps. Obviously the user must log in his Google account, and eXe must ask permission to upload files, ideally just once.

The first and second steps are handled by Google's OAuth 2.0 flow, we just have to trigger the OAuth 2.0 and retrieve the resulting authorization token. The third step is already implemented in the export to Self-contained Folder. Fourth step involves iterating through the exported files, and invoking Google Drive API to upload each one of them (requires the retrieved auth token).

Prototype

In the FR2292-export-gdrive branch, there is a rudimentary but working prototype illustrating the operation of this feature.

Authorization with OAuth

Using OAuth 2.0 client-side flow (Javascript) to authorize an application is easy and straight forward, but it has some serious issues. We need to first create an eXe project in the Google Developers Console, enable the Drive API and generate a client ID for web apps. This requires to declare the URLs where the OAuth 2.0 flow will be triggered from (Javascript origin URL). eXe is launched in http://127.0.0.1:51235, wich is not a accepted as a valid Javascript origin. Since http://localhost:51235 is accepted as a valid Javascript origin, that is the configuration that the actual prototype uses. It has a few problems:

  • The 51235 port can change if there are many eXe instances opened, or if some other process is using it. Theoretically we could declare more than one javascript origin: http://127.0.0.1:51235, http://127.0.0.1:51236, http://127.0.0.1:51237, etc. and/or hide the Export to Google Drive option if eXe has started in an URL not listed in the Google Developers Console
  • In this mode, client id will be set in a client-side script, so it cannot be kept secret. Any other app using http://localhost:51235 as its home URL could use our credentials and pose as eXe to fool the user, wich will be authorizing that other app instead of eXe. This is critical, the prototype auth flow should not and will not be used in production.
Using OAuth 2.0 for installed apps does not depend on eXe using a particular port and would solve the first problem, but any installed application that would use our client_id and client_secret could still ask the user permissions to their Google Services in eXe's name. In the documentation examples those keys are hard-coded in source code, wich in our case is public too. In compiled applications, public source code could use some dummy client_id and client_secret, that would be replaced with the official ones on building the compiled binaries (which would be not human readable). But Python 'binaries' are basically clear text, so the client_secret would be still public. So far I cannot figure out how to keep client_secret, secret.

Note that if eXe would be executed as a hosted web app, with a known URL (i.e. https://editor.exelearning.net), the client-side flow would be perfectly safe, because even if client_id is public, it cannot be used from an unauthorized URL.

Dependencies

This solution to publish in Google Drive use the following external libraries:

  • Google APIs Client Library for JavaScript. The prototype uses the on-line library, so there is no need to solve dependencies on installation. This will work as long as the eXe app has internet connection, which is anyway needed if the user wants to publish files in GDrive.
<script type="text/javascript" src="https://apis.google.com/js/client.js"></script>

Unknown Mimetypes

The insert method of the Google Drive API, used to upload a file, accepts an mimeType property in the file metadata. Though the documentation states that "This field can be left blank", so far I cannot get the application to upload a file without that attribute set to something different than None.

Examples in Google Documentation, and the actual prototype, use mimetypes.guess_type to guess the mimeType for the most common extensions (*.html, *.png, *.jpg, *.css, ...). The problem is that the every exported web site holds a content.data file, and mimetypes.guess_type does not recognize the .data extension. If the mimeType property would be really optional, content.data should be uploaded wihtout setting it, but when I tried that, an error was thrown. My guess is that the library validations are more strict than the Google Drive API. Maybe there is some workaround this.

Right now, the prototype just skips this file and shows a warning to the user. As far as I know, this file is not needed to use the generated content, but it is needed to import the exported site back into eXe for editing.