Source: box-sdk.js

'use strict';

/*
 * https://github.com/adityamukho/node-box-sdk
 *
 * Copyright (c) 2014 Aditya Mukhopadhyay
 * Licensed under the MIT license.
 */

/**
 * The Box SDK entry point.
 * @example
 * var box_sdk = require('box-sdk');
 * @module box-sdk
 */

/**
 * A callback with an optional error argument.
 * @callback optionalErrorCallback
 * @param {Error} [error] - Any error that occurred while attempting to stop the server.
 */

/**
 * A Writable Stream.
 * @external Writable
 * @see {@link http://nodejs.org/api/stream.html#stream_class_stream_writable}
 */

var base = require('base-framework'),
	_ = require('lodash'),
	Log = require('log'),
	Connection = require('./connector');

/**
 * Constructor options for the Box object.
 * @typedef {Object} BoxInit
 * @property {string} client_id - The Box app's Client ID.
 * @property {string} client_secret - The Box app's Client Secret.
 * @property {number} port - The port on which to listen for the authorization callback.
 * @property {string} [host] - Optional host on which to listen for the authorization callback.
 * Defaults to {@linkcode localhost}.
 */

/**
 * @class Box
 * @classdesc The Box object: One instance for each client. This is used to get {@link Connection|connections}.
 * @param {?BoxInit} [opts] - Client parameters required for standalone operation. Should be omitted when
 * running with [passport authentication middleware]{@link https://github.com/bluedge/passport-box}.
 * @param {?string} [logLevel] - Optional log level. Defaults to {@linkcode info}.
 * @param {?external:Writable} [logStream] - Optional writable stream for log output. Defaults to console.
 * @see {@link https://www.npmjs.org/package/log#log-levels}
 */
var Box = base.createChild().addInstanceMethods(
	/** @lends Box.prototype */
	{
		init: function (opts, logLevel, logStream) {
			var self = this;
			self.connections = {};

			if (opts) {
				if (!opts.client_id) {
					throw new Error('Must specify a client_id');
				}
				if (!opts.client_secret) {
					throw new Error('Must specify a client_secret');
				}

				self.port = parseInt(opts.port, 10);
				if (!_.isNumber(self.port)) {
					throw new Error('Must specify a numeric port');
				}

				self.host = opts.host || 'localhost';
				self.log = new Log(logLevel || 'info', logStream);
				self.client_id = opts.client_id;
				self.client_secret = opts.client_secret;

				var router = require('router'),
					http = require('http'),
					request = require('request'),
					url = require('url');

				var route = router();
				route.get('/authorize', function (req, res) {
					var params = url.parse(req.url, true).query;
					if (!self.connections[params.id]) {
						params.error = 'invalid_request';
						params.error_description = "Could not identify a connection for this request.";
					} else if (params.state !== self.connections[params.id].csrf) {
						params.error = 'forged_request';
						params.error_description = "This looks like a CSRF attack. Someone may be trying to access your account illegaly.";
					}
					if (params.error) {
						var message = params.error_description || "Unknown error";
						self.log.error(
							"Error authenticating user - Error Code: %s, Error Description: %s",
							params.error, message
						);
						var ecode;
						switch (params.error) {
						case 'access_denied':
						case 'forged_request':
							ecode = 401;
							break;
						case 'invalid_request':
						case 'unsupported_response_type':
							ecode = 400;
							break;
						case 'server_error':
							ecode = 500;
							break;
						}
						res.writeHead(ecode, message);
						res.end('Error ' + ecode + ': ' + message);
					} else {
						res.writeHead(200);
						res.end("Authorization code received.");

						var tokenParams = {
								client_id: self.client_id,
								client_secret: self.client_secret,
								grant_type: 'authorization_code',
								code: params.code
							},
							authUrl = 'https://www.box.com/api/oauth2/token';

						request.post({
							url: authUrl,
							form: tokenParams,
							json: true
						}, function (err, res, body) {
							if (err) {
								/**
								 * Fires when an error occurs while setting access tokens have been set on this
								 * connection. Could be triggered more than once, so listeners must deregister
								 * after receiving the first event. Preferably use the {@link Connection#ready} method.
								 * @event Connection#"tokens.error"
								 * @type {Error}
								 * @see {@link Connection#ready}
								 */
								self.connections[params.id].emit('tokens.error', err);
							} else {
								self.connections[params.id]._setTokens(body);
							}
						});
					}
				});

				self.server = http.createServer(route);
				self.server.listen(self.port, self.host, function () {
					self.log.info('Box SDK listening at http://%s:%d', self.host, self.port);
				});

				//Track connections
				self.sockets = {};
				self.server.on('connection', function (socket) {
					self.sockets[socket.fd] = socket;
					socket.on('close', function () {
						delete self.sockets[socket.fd];
					});
				});
			}
		},

		/**
		 * Get a connection for the provided email id.
		 * @param {string} email - The email account identifier to connect to.
		 * @returns {Connection} A connection on which API calls can be made.
		 * @fires Connection#"tokens.set"
		 */
		getConnection: function (email) {
			if (this.connections[email]) {
				return this.connections[email];
			}

			var connection = new Connection(this, email);
			this.connections[email] = connection;
			return connection;
		},

		/**
		 * Used with Passport {@link https://github.com/bluedge/passport-box|BoxStrategy}.
		 * @returns {function} A callback for use in the BoxStrategy.
		 * @example
		 * passport.use(new BoxStrategy({
		 *   clientID: BOX_CLIENT_ID,
		 *   clientSecret: BOX_CLIENT_SECRET,
		 *   callbackURL: "http://127.0.0.1:3000/auth/box/callback"
		 * }, box.authenticate()));
		 */
		authenticate: function () {
			return _.bind(function (access_token, refresh_token, profile, done) {
				var email = profile.login,
					connection = this.getConnection(email);
				connection._setTokens({
					access_token: access_token,
					refresh_token: refresh_token
				});
				return done(null, profile);
			}, this);
		},

		/**
		 * Stop the authorization callback listener when running in standalone mode.
		 * @param {optionalErrorCallback} callback - Called after asynchronously stopping the server.
		 */
		stopServer: function (callback) {
			if (this.server) {
				this.log.debug('Stopping server...');
				try {
					this.server.close();
					_.forIn(this.sockets, function (socket) {
						socket.destroy();
					});
					callback();
				} catch (err) {
					callback(err);
				}
			}
		}
	});

/**
 * The {@link Box} prototype, instances of which represent a specific client.
 * @example
 * var box = box_sdk.Box({
 *   client_id: 'client id',
 *   client_secret: 'client secret',
 *   port: 9999
 * });
 */
exports.Box = Box;

/**
 * The {@link Connection} prototype, instances of which represent a specific account.
 */
exports.Connection = Connection;
Copyright © 2014-2015 Aditya Mukhopadhyay
Documentation generated by JSDoc 3.2.2 on Sun Jul 27 2014 08:27:52 GMT+0530 (IST) using the DocStrap template.