<commit_before>var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors');
var mbaasApi = require('fh-mbaas-api');
var mbaasExpress = mbaasApi.mbaasExpress();
var queryString = require('querystring');
var request = require('request');

function auth() {
  var router = express.Router();

  router.all('/init', function(req, res) {
        var params = req.body; // params.userId params.password
        if (params && params.userId) {
          console.log('Checking credentials for user ' + params.userId);
          initSession(params.userId, function(err, result){
            if (err) {
              console.log('Invalid credentials for user ' + params.userId);
              res.status(400);
              res.json({message: 'Invalid credentials'});
              return;
            } else {
              res.json({
                status: 'ok',
                userId: params.userId,
                sessionToken: params.userId + '_sessiontoken',
                authResponse: result
              });
              return;
            }
          });

        } else {
          res.status(400);
          res.json({message: params.userId});
        }

  });

  /**
   * Validate the user's session info with Google, and create a session object.
   * This will also create a account object if necessary.
   *
   * @param {String} idToken an idToken provided by an Android Client (see : https://developers.google.com/identity/sign-in/android/backend-auth)
   * @param {Callback} nodeCallback a node callback
   */
  function initSession(idToken, nodeCallback) {
      //validate with google
      var options = {

          url: 'https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=' + (idToken),
          method: 'GET'
      };

      request(options, handleGoogleResponse(idToken, nodeCallback)).end();

  }

  /**
   *
   * This callback consumes Google's response, and performs the appropriate
   * actions on the session. (Set session optionally creating the account).
   *
   * @param {type} idToken an idToken received from Android
   * @param {type} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function handleGoogleResponse(idToken, nodeCallback) {
      return function (error, response, body) {

          //the whole response has been recieved, so we just print it out here
          if (error) {
           nodeCallback(e);
          } else {
            var resObject = JSON.parse(body);
            if (resObject.error_description) {
              nodeCallback(resObject.error_description, null);
            } else {
              checkAccount(resObject, idToken, nodeCallback);
            }
          }

      };
  }

  /**
   * Creates a user if one does not exist
   *
   * @param {Object}  Google user Token response (see : https://developers.google.com/identity/sign-in/android/backend-auth#calling-the-tokeninfo-endpoint)
   * @param {Sting} sessionToken
   * @param {NodeCallback} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function checkAccount(googleUserToken, sessionToken, nodeCallback) {
      console.log('checkAccountAndCreateSession');
      var handleResults = function (error, responseData) {
        console.log('checkAccountAndCreateSession_callback');
          if (error) {
              nodeCallback(error, null);
          } else {
              if (!responseData || responseData.count === 0) {
                  //no account, must create.
                  createAccount(googleUserToken, nodeCallback);
              } else if (responseData.count > 1) {
                  nodeCallback("error : Too many accounts returned", null);
              } else {
                  nodeCallback(null, googleUserToken.sub);
              }
          }
      };

      mbaasApi.db({
          "act": "list",
          "type": 'account',
          "eq": {"id": googleUserToken.sub},
      }, handleResults);
  }

  /**
   *  Creates an account and sets the session to that account.
   * @param {Object}  Google user Token response (see : https://developers.google.com/identity/sign-in/android/backend-auth#calling-the-tokeninfo-endpoint)
   * @param {NodeCallback} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function createAccount(googleUserToken, nodeCallback) {
    authResponse.authToken = '';
      mbaasApi.db({
          "act": "create",
          "type": 'account',
          "fields": [googleUserToken]
      }, function (error, data) {
        console.log('createAccountAndSetSession_cb');
          if (error) {
               nodeCallback(error);
          } else {
               nodeCallback(null, googleUserToken.sub);
          }
      });
  }

  return router;

}

module.exports = auth;
<commit_msg>update file /lib/auth.js<commit_after>var express = require('express');
var bodyParser = require('body-parser');
var cors = require('cors');
var mbaasApi = require('fh-mbaas-api');
var mbaasExpress = mbaasApi.mbaasExpress();
var queryString = require('querystring');
var request = require('request');

function auth() {
  var router = express.Router();

  router.all('/init', function(req, res) {
        var params = req.body; // params.userId params.password
        if (params && params.userId) {
          console.log('Checking credentials for user ' + params.userId);
          initSession(params.userId, function(err, result){
            if (err) {
              console.log('Invalid credentials for user ' + params.userId);
              res.status(400);
              res.json({message: 'Invalid credentials'});
              return;
            } else {
              console.log('Logged in ' + params.userId);
              console.log('Result ' + result);
              res.json({
                status: 'ok',
                userId: params.userId,
                sessionToken: params.userId + '_sessiontoken',
                authResponse: result
              });
              return;
            }
          });

        } else {
          res.status(400);
          res.json({message: params.userId});
        }

  });

  /**
   * Validate the user's session info with Google, and create a session object.
   * This will also create a account object if necessary.
   *
   * @param {String} idToken an idToken provided by an Android Client (see : https://developers.google.com/identity/sign-in/android/backend-auth)
   * @param {Callback} nodeCallback a node callback
   */
  function initSession(idToken, nodeCallback) {
      //validate with google
      var options = {

          url: 'https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=' + (idToken),
          method: 'GET'
      };

      request(options, handleGoogleResponse(idToken, nodeCallback)).end();

  }

  /**
   *
   * This callback consumes Google's response, and performs the appropriate
   * actions on the session. (Set session optionally creating the account).
   *
   * @param {type} idToken an idToken received from Android
   * @param {type} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function handleGoogleResponse(idToken, nodeCallback) {
      return function (error, response, body) {

          //the whole response has been recieved, so we just print it out here
          if (error) {
           nodeCallback(e);
          } else {
            var resObject = JSON.parse(body);
            if (resObject.error_description) {
              nodeCallback(resObject.error_description, null);
            } else {
              checkAccount(resObject, idToken, nodeCallback);
            }
          }

      };
  }

  /**
   * Creates a user if one does not exist
   *
   * @param {Object}  Google user Token response (see : https://developers.google.com/identity/sign-in/android/backend-auth#calling-the-tokeninfo-endpoint)
   * @param {Sting} sessionToken
   * @param {NodeCallback} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function checkAccount(googleUserToken, sessionToken, nodeCallback) {
      console.log('checkAccountAndCreateSession');
      var handleResults = function (error, responseData) {
        console.log('checkAccountAndCreateSession_callback');
          if (error) {
              nodeCallback(error, null);
          } else {
              if (!responseData || responseData.count === 0) {
                  //no account, must create.
                  createAccount(googleUserToken, nodeCallback);
              } else if (responseData.count > 1) {
                  nodeCallback("error : Too many accounts returned", null);
              } else {
                  nodeCallback(null, googleUserToken.sub);
              }
          }
      };

      mbaasApi.db({
          "act": "list",
          "type": 'account',
          "eq": {"id": googleUserToken.sub},
      }, handleResults);
  }

  /**
   *  Creates an account and sets the session to that account.
   * @param {Object}  Google user Token response (see : https://developers.google.com/identity/sign-in/android/backend-auth#calling-the-tokeninfo-endpoint)
   * @param {NodeCallback} nodeCallback
   * @returns session Data on callback if successful, error on callback otherwise
   */
  function createAccount(googleUserToken, nodeCallback) {
    authResponse.authToken = '';
      mbaasApi.db({
          "act": "create",
          "type": 'account',
          "fields": [googleUserToken]
      }, function (error, data) {
        console.log('createAccountAndSetSession_cb');
          if (error) {
               nodeCallback(error);
          } else {
               nodeCallback(null, googleUserToken.sub);
          }
      });
  }

  return router;

}

module.exports = auth;
<|endoftext|>


<commit_before>import Ember from 'ember';
import canUseDOM from 'ember-in-viewport/utils/can-use-dom';
import canUseRAF from 'ember-in-viewport/utils/can-use-raf';
import isInViewport from 'ember-in-viewport/utils/is-in-viewport';
import checkScrollDirection from 'ember-in-viewport/utils/check-scroll-direction';

const {
  Mixin,
  setProperties,
  merge,
  typeOf,
  assert,
  on,
  $,
  get,
  set,
  run: { scheduleOnce, debounce, bind, next },
  computed: { not }
} = Ember;
const defaultListeners = [
  { context: window, event: 'scroll.scrollable' },
  { context: window, event: 'resize.resizable' },
  { context: document, event: 'touchmove.scrollable' }
];
const rAFIDS = {};
const lastDirection = {};
const lastPosition  = {};

export default Mixin.create({
  viewportExited: not('viewportEntered').readOnly(),

  _setInitialState: on('init', function() {
    const options = merge({
      viewportUseRAF: canUseRAF(),
      viewportEntered: false,
      viewportListeners: defaultListeners
    }, this._buildOptions());

    setProperties(this, options);
  }),

  _buildOptions(defaultOptions = {}) {
    if (this.container) {
      return merge(defaultOptions, this.container.lookup('config:in-viewport'));
    }
  },

  _setupElement: on('didInsertElement', function() {
    if (!canUseDOM) {
      return;
    }

    this._setInitialViewport(window);
    this._addObserverIfNotSpying();
    this._bindScrollDirectionListener(window, get(this, 'viewportScrollSensitivity'));

    const listeners = get(this, 'viewportListeners');

    if (!get(this, 'viewportUseRAF')) {
      listeners.forEach((listener) => {
        const { context, event } = listener;
        this._bindListeners(context, event);
      });
    }
  }),

  _teardown: on('willDestroyElement', function() {
    this._unbindListeners();
  }),

  _addObserverIfNotSpying() {
    if (!get(this, 'viewportSpy')) {
      this.addObserver('viewportEntered', this, this._unbindIfEntered);
    }
  },

  _setViewportEntered(context = null) {
    assert('You must pass a valid context to _setViewportEntered', context);

    const element = get(this, 'element');

    if (!element) {
      return;
    }

    const elementId = get(this, 'elementId');
    const viewportUseRAF = get(this, 'viewportUseRAF');
    const viewportTolerance = get(this, 'viewportTolerance');
    const $contextEl = $(context);
    const height = $contextEl.height();
    const width = $contextEl.width();
    const boundingClientRect = element.getBoundingClientRect();

    this._triggerDidAccessViewport(
      isInViewport(boundingClientRect, height, width, viewportTolerance)
    );

    if (boundingClientRect && viewportUseRAF) {
      rAFIDS[elementId] = window.requestAnimationFrame(
        bind(this, this._setViewportEntered, context)
      );
    }
  },

  _triggerDidScrollDirection($contextEl = null, sensitivity = 1) {
    assert('You must pass a valid context element to _triggerDidScrollDirection', $contextEl);
    assert('sensitivity cannot be 0', sensitivity);

    const elementId = get(this, 'elementId');
    const viewportEntered = get(this, 'viewportEntered');
    const lastDirectionForEl = lastDirection[elementId];
    const lastPositionForEl = lastPosition[elementId];
    const newPosition = {
      top: $contextEl.scrollTop(),
      left: $contextEl.scrollLeft()
    };

    const scrollDirection = checkScrollDirection(lastPositionForEl, newPosition, sensitivity);
    const directionChanged = scrollDirection !== lastDirectionForEl;

    if (scrollDirection && directionChanged && viewportEntered) {
      this.trigger('didScroll', scrollDirection);
      lastDirection[elementId] = scrollDirection;
    }

    lastPosition[elementId] = newPosition;
  },

  _triggerDidAccessViewport(hasEnteredViewport = false) {
    const viewportEntered = get(this, 'viewportEntered');
    const didEnter = !viewportEntered && hasEnteredViewport;
    const didLeave = viewportEntered && !hasEnteredViewport;
    let triggeredEventName = '';

    if (didEnter) {
      triggeredEventName = 'didEnterViewport';
    }

    if (didLeave) {
      triggeredEventName = 'didExitViewport';
    }

    set(this, 'viewportEntered', hasEnteredViewport);

    this.trigger(triggeredEventName);
  },

  _unbindIfEntered() {
    if (!get(this, 'viewportSpy') && get(this, 'viewportEntered')) {
      this._unbindListeners();
      this.removeObserver('viewportEntered', this, this._unbindIfEntered);
      set(this, 'viewportEntered', true);
    }
  },

  _setInitialViewport(context = null) {
    assert('You must pass a valid context to _setInitialViewport', context);

    return scheduleOnce('afterRender', this, () => {
      this._setViewportEntered(context);
    });
  },

  _debouncedEventHandler(methodName, ...args) {
    assert('You must pass a methodName to _debouncedEventHandler', methodName);
    assert('methodName must be a string', typeOf(methodName) === 'string');

    debounce(this, () => {
      this[methodName](...args);
    }, get(this, 'viewportRefreshRate'));
  },

  _bindScrollDirectionListener(context = null, sensitivity = 1) {
    assert('You must pass a valid context to _bindScrollDirectionListener', context);
    assert('sensitivity cannot be 0', sensitivity);

    const $contextEl = $(context);

    $contextEl.on(`scroll.directional#${get(this, 'elementId')}`, () => {
      this._debouncedEventHandler('_triggerDidScrollDirection', $contextEl, sensitivity);
    });
  },

  _unbindScrollDirectionListener(context = null) {
    assert('You must pass a valid context to _bindScrollDirectionListener', context);

    const elementId = get(this, 'elementId');

    $(context).off(`scroll.directional#${elementId}`);
    delete lastPosition[elementId];
    delete lastDirection[elementId];
  },

  _bindListeners(context = null, event = null) {
    assert('You must pass a valid context to _bindListeners', context);
    assert('You must pass a valid event to _bindListeners', event);

    $(context).on(`${event}.${get(this, 'elementId')}`, () => {
      this._debouncedEventHandler('_setViewportEntered', context);
    });
  },

  _unbindListeners() {
    const elementId = get(this, 'elementId');
    const listeners = get(this, 'viewportListeners');

    if (get(this, 'viewportUseRAF')) {
      next(this, () => {
        window.cancelAnimationFrame(rAFIDS[elementId]);
        delete rAFIDS[elementId];
      });
    }

    listeners.forEach((listener) => {
      const { context, event } = listener;
      $(context).off(`${event}.${elementId}`);
    });

    this._unbindScrollDirectionListener(window);
  }
});
<commit_msg>Remove Ember.on listeners<commit_after>import Ember from 'ember';
import canUseDOM from 'ember-in-viewport/utils/can-use-dom';
import canUseRAF from 'ember-in-viewport/utils/can-use-raf';
import isInViewport from 'ember-in-viewport/utils/is-in-viewport';
import checkScrollDirection from 'ember-in-viewport/utils/check-scroll-direction';

const {
  Mixin,
  setProperties,
  merge,
  typeOf,
  assert,
  $,
  get,
  set,
  run: { scheduleOnce, debounce, bind, next },
  computed: { not }
} = Ember;
const defaultListeners = [
  { context: window, event: 'scroll.scrollable' },
  { context: window, event: 'resize.resizable' },
  { context: document, event: 'touchmove.scrollable' }
];
const rAFIDS = {};
const lastDirection = {};
const lastPosition  = {};

export default Mixin.create({
  viewportExited: not('viewportEntered').readOnly(),

  init() {
    this._super(...arguments);
    const options = merge({
      viewportUseRAF: canUseRAF(),
      viewportEntered: false,
      viewportListeners: defaultListeners
    }, this._buildOptions());

    setProperties(this, options);
  },

  didInsertElement() {
    this._super(...arguments);

    if (!canUseDOM) {
      return;
    }

    this._setInitialViewport(window);
    this._addObserverIfNotSpying();
    this._bindScrollDirectionListener(window, get(this, 'viewportScrollSensitivity'));

    if (!get(this, 'viewportUseRAF')) {
      get(this, 'viewportListeners').forEach((listener) => {
        const { context, event } = listener;
        this._bindListeners(context, event);
      });
    }
  },

  willDestroyElement() {
    this._super(...arguments);
    this._unbindListeners();
  },

  _buildOptions(defaultOptions = {}) {
    if (this.container) {
      return merge(defaultOptions, this.container.lookup('config:in-viewport'));
    }
  },

  _addObserverIfNotSpying() {
    if (!get(this, 'viewportSpy')) {
      this.addObserver('viewportEntered', this, this._unbindIfEntered);
    }
  },

  _setViewportEntered(context = null) {
    assert('You must pass a valid context to _setViewportEntered', context);

    const element = get(this, 'element');

    if (!element) {
      return;
    }

    const $contextEl = $(context);
    const boundingClientRect = element.getBoundingClientRect();

    this._triggerDidAccessViewport(
      isInViewport(
        boundingClientRect,
        $contextEl.height(),
        $contextEl.width(),
        get(this, 'viewportTolerance')
      )
    );

    if (boundingClientRect && get(this, 'viewportUseRAF')) {
      rAFIDS[get(this, 'elementId')] = window.requestAnimationFrame(
        bind(this, this._setViewportEntered, context)
      );
    }
  },

  _triggerDidScrollDirection($contextEl = null, sensitivity = 1) {
    assert('You must pass a valid context element to _triggerDidScrollDirection', $contextEl);
    assert('sensitivity cannot be 0', sensitivity);

    const elementId = get(this, 'elementId');
    const lastDirectionForEl = lastDirection[elementId];
    const lastPositionForEl = lastPosition[elementId];
    const newPosition = {
      top: $contextEl.scrollTop(),
      left: $contextEl.scrollLeft()
    };

    const scrollDirection = checkScrollDirection(lastPositionForEl, newPosition, sensitivity);
    const directionChanged = scrollDirection !== lastDirectionForEl;

    if (scrollDirection && directionChanged && get(this, 'viewportEntered')) {
      this.trigger('didScroll', scrollDirection);
      lastDirection[elementId] = scrollDirection;
    }

    lastPosition[elementId] = newPosition;
  },

  _triggerDidAccessViewport(hasEnteredViewport = false) {
    const viewportEntered = get(this, 'viewportEntered');
    const didEnter = !viewportEntered && hasEnteredViewport;
    const didLeave = viewportEntered && !hasEnteredViewport;
    let triggeredEventName = '';

    if (!didEnter && !didLeave) {
      return;
    }

    if (didEnter) {
      triggeredEventName = 'didEnterViewport';
    }

    if (didLeave) {
      triggeredEventName = 'didExitViewport';
    }

    set(this, 'viewportEntered', hasEnteredViewport);

    this.trigger(triggeredEventName);
  },

  _unbindIfEntered() {
    if (!get(this, 'viewportSpy') && get(this, 'viewportEntered')) {
      this._unbindListeners();
      this.removeObserver('viewportEntered', this, this._unbindIfEntered);
      set(this, 'viewportEntered', true);
    }
  },

  _setInitialViewport(context = null) {
    assert('You must pass a valid context to _setInitialViewport', context);

    return scheduleOnce('afterRender', this, () => {
      this._setViewportEntered(context);
    });
  },

  _debouncedEventHandler(methodName, ...args) {
    assert('You must pass a methodName to _debouncedEventHandler', methodName);
    assert('methodName must be a string', typeOf(methodName) === 'string');

    debounce(this, () => this[methodName](...args), get(this, 'viewportRefreshRate'));
  },

  _bindScrollDirectionListener(context = null, sensitivity = 1) {
    assert('You must pass a valid context to _bindScrollDirectionListener', context);
    assert('sensitivity cannot be 0', sensitivity);

    const $contextEl = $(context);

    $contextEl.on(`scroll.directional#${get(this, 'elementId')}`, () => {
      this._debouncedEventHandler('_triggerDidScrollDirection', $contextEl, sensitivity);
    });
  },

  _unbindScrollDirectionListener(context = null) {
    assert('You must pass a valid context to _bindScrollDirectionListener', context);

    const elementId = get(this, 'elementId');

    $(context).off(`scroll.directional#${elementId}`);
    delete lastPosition[elementId];
    delete lastDirection[elementId];
  },

  _bindListeners(context = null, event = null) {
    assert('You must pass a valid context to _bindListeners', context);
    assert('You must pass a valid event to _bindListeners', event);

    $(context).on(`${event}.${get(this, 'elementId')}`, () => {
      this._debouncedEventHandler('_setViewportEntered', context);
    });
  },

  _unbindListeners() {
    const elementId = get(this, 'elementId');

    if (get(this, 'viewportUseRAF')) {
      next(this, () => {
        window.cancelAnimationFrame(rAFIDS[elementId]);
        delete rAFIDS[elementId];
      });
    }

    get(this, 'viewportListeners').forEach((listener) => {
      const { context, event } = listener;
      $(context).off(`${event}.${elementId}`);
    });

    this._unbindScrollDirectionListener(window);
  }
});
<|endoftext|>


<commit_before>/*
    The Auth module manages the user authentication and provides
    controllers to easily bind it to the routes.
*/

var OAuth = require('oauth').OAuth;

module.exports = {

    /*
     * Attributes
     */

    manager: null,
    oauth: null,

    /*
     * Initialization
     */

    init: function(config, manager) {
        this.manager = manager;
        this.config = config;
        this.oauth = new OAuth(
            'https://api.twitter.com/oauth/request_token',
            'https://api.twitter.com/oauth/access_token',
            config.application.consumer_key,
            config.application.consumer_secret,
            '1.0',
            config.application.callback_url,
            'HMAC-SHA1'
        );

        // We need to redefine the "this" reference or the controllers won't find the "oauth" object
        for(var i in this.controllers) {
            this.controllers[i] = this.controllers[i](this);
        }
    },

    /*
     * Controllers : each controller needs to be inside a closure so the "this" keyword could be avoided
     * and the "_this" reference will be used instead
     */

    controllers: {

        auth: function(_this) {
            return function(req, res, next) {
                _this.oauth.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret) {
                    if(!error) {
                        req.session.token = oauth_token;
                        req.session.token_secret = oauth_token_secret;
                        res.redirect('https://twitter.com/oauth/authenticate?oauth_token=' + oauth_token);
                    }
                    else {
                        console.log('Request token failure'); // We need to log this, it's an important error.
                        next(); // Sends to the route displaying the issue to the client
                    }
                });
            };
        },

        callback: function(_this) {
            return function(req, res, next) {
                if(req.session.token) {

                    var oauth = req.session;
                    req.session.verifier = req.query.oauth_verifier;

                    _this.oauth.getOAuthAccessToken(oauth.token, oauth.token_secret, oauth.verifier,

                        function(error, oauth_access_token, oauth_access_token_secret, results){
                            var primary_access_token = _this.config.primary_account.access_token,
                                primary_access_token_secret = _this.config.primary_account.access_token_secret;

                            // The user should not be the primary account
                            if(!error && oauth_access_token != primary_access_token && oauth_access_token_secret != primary_access_token_secret){
                                req.session.id = results.user_id;

                                // These are no longer necessary :
                                req.session.token = null;
                                req.session.token_secret = null;

                                // We inform the manager that this user can be used
                                _this.manager.set(results.user_id, {
                                    name: results.screen_name,
                                    access_token: oauth_access_token,
                                    access_token_secret: oauth_access_token_secret
                                });

                                res.locals.status = 0; // 0 = OK
                            } else {
                                if(oauth_access_token == primary_access_token && oauth_access_token_secret == primary_access_token_secret) {
                                    console.log("WARNING: You can't add the primary account to the user list!");
                                }

                                res.locals.status = 1; // 1 = No access to the user
                            }

                            next();
                        }

                    );

                } else {
                    res.locals.status = 2; // 2 = Forbidden
                    next();
                }
            };
        }
    }

};<commit_msg>The "request token failure" error is now identified as an error in the logs<commit_after>/*
    The Auth module manages the user authentication and provides
    controllers to easily bind it to the routes.
*/

var OAuth = require('oauth').OAuth;

module.exports = {

    /*
     * Attributes
     */

    manager: null,
    oauth: null,

    /*
     * Initialization
     */

    init: function(config, manager) {
        this.manager = manager;
        this.config = config;

        this.oauth = new OAuth(
            'https://api.twitter.com/oauth/request_token',
            'https://api.twitter.com/oauth/access_token',
            config.application.consumer_key,
            config.application.consumer_secret,
            '1.0',
            config.application.callback_url,
            'HMAC-SHA1'
        );

        // We need to redefine the "this" reference or the controllers won't find the "oauth" object
        for(var i in this.controllers) {
            this.controllers[i] = this.controllers[i](this);
        }
    },

    /*
     * Controllers : each controller needs to be inside a closure so the "this" keyword could be avoided
     * and the "_this" reference will be used instead
     */

    controllers: {

        auth: function(_this) {
            return function(req, res, next) {
                _this.oauth.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret) {
                    if(!error) {
                        req.session.token = oauth_token;
                        req.session.token_secret = oauth_token_secret;
                        res.redirect('https://twitter.com/oauth/authenticate?oauth_token=' + oauth_token);
                    }
                    else {
                        console.log('ERROR: Request token failure');
                        next(); // Sends to the route displaying the issue to the client
                    }
                });
            };
        },

        callback: function(_this) {
            return function(req, res, next) {
                if(req.session.token) {

                    var oauth = req.session;
                    req.session.verifier = req.query.oauth_verifier;

                    _this.oauth.getOAuthAccessToken(oauth.token, oauth.token_secret, oauth.verifier,

                        function(error, oauth_access_token, oauth_access_token_secret, results){
                            var primary_access_token = _this.config.primary_account.access_token,
                                primary_access_token_secret = _this.config.primary_account.access_token_secret;

                            // The user should not be the primary account
                            if(!error && oauth_access_token != primary_access_token && oauth_access_token_secret != primary_access_token_secret){
                                req.session.id = results.user_id;

                                // These are no longer necessary :
                                req.session.token = null;
                                req.session.token_secret = null;

                                // We inform the manager that this user can be used
                                _this.manager.set(results.user_id, {
                                    name: results.screen_name,
                                    access_token: oauth_access_token,
                                    access_token_secret: oauth_access_token_secret
                                });

                                res.locals.status = 0; // 0 = OK
                            } else {
                                if(oauth_access_token == primary_access_token && oauth_access_token_secret == primary_access_token_secret) {
                                    console.log("WARNING: You can't add the primary account to the user list!");
                                }

                                res.locals.status = 1; // 1 = No access to the user
                            }

                            next();
                        }

                    );

                } else {
                    res.locals.status = 2; // 2 = Forbidden
                    next();
                }
            };
        }
    }

};<|endoftext|>


<commit_before>import DS from 'ember-data';
import isJavascript from '../lib/is-js';
import RSVP from 'rsvp';
import computed from 'ember-computed';
import inject from 'ember-service/inject';

export default DS.Model.extend({
  htmlParser: inject(),
  assets: inject(),
  text: DS.attr(),
  dom: DS.attr(),

  document: computed('text', function() {
    return this.get('htmlParser').parse(this.get('text'));
  }),

  pieces: computed('document', function() {
    return this._separateScripts();
  }),

  _separateScripts() {
    let doc = this.get('document');
    let body = doc.querySelector('body');
    let scripts = [];

    // First handle <script> in the <head>
    Array.from(doc.querySelectorAll('head script')).forEach(script => {
      if (isJavascript(script)) {
        // Save for later evaluation
        scripts.push(script);
      } else {
        // Non-javascript script tags (templates, for example) get
        // moved from head to body so they will get added to our
        // rendered output (we only output the contents of body, since
        // it doesn't make sense to add a new head to the existing
        // page).
        body.appendChild(script);
      }
    });

    // Then handle <script> in <body>
    Array.from(body.querySelectorAll('script')).forEach(script => {
      if (isJavascript(script)) {
        // TODO: compatability: http://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove.html
        script.remove();
        scripts.push(script);
      }
    });

    // Styles, both inline and external, with their relative order maintained.
    let styles = Array.from(doc.querySelectorAll('style, link[rel=stylesheet]')).map(element => element);

    // Remove the style tags from our imported body, because they will be handled separately.
    // TODO: compatability: http://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove.html
    Array.from(body.querySelectorAll('style, link[rel=stylesheet]')).forEach(element => element.remove());

    return { body, scripts, styles };
  },

  title: computed('document', function() {
    let titleTag = this.get('document').querySelector('title');
    if (titleTag) {
      return titleTag.innerHTML;
    }
  }),

  bodyClasses: computed('document', function() {
    let titleTag = this.get('document').querySelector('title');
    if (titleTag) {
      return titleTag.innerHTML;
    }
  }),

  appendTo($element) {
    if (this.get('dom')) {
      this.get('dom').forEach(node => $element[0].appendChild(node));
      return RSVP.resolve();
    }

    let assets = this.get('assets');
    let scriptsPromise = assets.applyScripts(this.get('pieces.scripts'));
    return assets.applyStyles(this.get('pieces.styles'), $element[0]).finally(() => {
      Array.from(this.get('pieces.body').childNodes).forEach(child => {
        $element[0].appendChild(assets.cloneOrImport(child));
      });
      return scriptsPromise;
    });
  }
});
<commit_msg>clone to prevent mutation of the model within the store<commit_after>import DS from 'ember-data';
import isJavascript from '../lib/is-js';
import RSVP from 'rsvp';
import computed from 'ember-computed';
import inject from 'ember-service/inject';

export default DS.Model.extend({
  htmlParser: inject(),
  assets: inject(),
  text: DS.attr(),
  dom: DS.attr(),

  document: computed('text', function() {
    return this.get('htmlParser').parse(this.get('text'));
  }),

  pieces: computed('document', function() {
    return this._separateScripts();
  }),

  _separateScripts() {
    let doc = this.get('document');
    let body = doc.querySelector('body');
    let scripts = [];

    // First handle <script> in the <head>
    Array.from(doc.querySelectorAll('head script')).forEach(script => {
      if (isJavascript(script)) {
        // Save for later evaluation
        scripts.push(script);
      } else {
        // Non-javascript script tags (templates, for example) get
        // moved from head to body so they will get added to our
        // rendered output (we only output the contents of body, since
        // it doesn't make sense to add a new head to the existing
        // page).
        body.appendChild(script);
      }
    });

    // Then handle <script> in <body>
    Array.from(body.querySelectorAll('script')).forEach(script => {
      if (isJavascript(script)) {
        // TODO: compatability: http://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove.html
        script.remove();
        scripts.push(script);
      }
    });

    // Styles, both inline and external, with their relative order maintained.
    let styles = Array.from(doc.querySelectorAll('style, link[rel=stylesheet]')).map(element => element);

    // Remove the style tags from our imported body, because they will be handled separately.
    // TODO: compatability: http://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove.html
    Array.from(body.querySelectorAll('style, link[rel=stylesheet]')).forEach(element => element.remove());

    return { body, scripts, styles };
  },

  title: computed('document', function() {
    let titleTag = this.get('document').querySelector('title');
    if (titleTag) {
      return titleTag.innerHTML;
    }
  }),

  bodyClasses: computed('document', function() {
    let titleTag = this.get('document').querySelector('title');
    if (titleTag) {
      return titleTag.innerHTML;
    }
  }),

  appendTo($element) {
    if (this.get('dom')) {
      this.get('dom').forEach(
        node => $element[0].appendChild(assets.cloneOrImport(node))
      );
      return RSVP.resolve();
    }

    let assets = this.get('assets');
    let scriptsPromise = assets.applyScripts(this.get('pieces.scripts'));
    return assets.applyStyles(this.get('pieces.styles'), $element[0]).finally(() => {
      Array.from(this.get('pieces.body').childNodes).forEach(child => {
        $element[0].appendChild(assets.cloneOrImport(child));
      });
      return scriptsPromise;
    });
  }
});
<|endoftext|>


<commit_before><commit_msg>Changed the error message associated with not providing a username.<commit_after><|endoftext|>


<commit_before>/*!
 * Copyright(c) 2010 Ciaran Jessup <ciaranj@gmail.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */
var connect= require('connect');
var fs= require('fs');
var StrategyExecutor= require('./strategyExecutor');

/**
 * Construct the authentication middleware.
 * Construction can take 2 forms:
 *    auth(<Strategy>()/[<Strategy>()])  -  A single configured strategy, or array of strategies.
 *    auth({strategies:<Strategy>()/[<Strategy>()...]})   - More powerful variant that allows for passing in other configuration options, none yet defined.
 */
Auth= module.exports = function(optionsOrStrategy) {

  var strategies;
  var options;
  if( !optionsOrStrategy ) throw new Error("You must specify at least one strategy to use the authentication middleware, even if it is anonymous.");
  // Constructor form 1
  if(  Array.isArray(optionsOrStrategy) || ( optionsOrStrategy.authenticate !== undefined  &&
      optionsOrStrategy.strategies === undefined  ) ) {
    strategies= Array.isArray(optionsOrStrategy) ? optionsOrStrategy : [optionsOrStrategy];
    options= {};
  }
  else {
    options= optionsOrStrategy
    strategies= Array.isArray(optionsOrStrategy.strategies) ? optionsOrStrategy.strategies : [optionsOrStrategy.strategies];
  }
  var isAuthenticated = function( request, scope ) {
    if( scope === undefined ) {
      return request.getAuthDetails().user !== undefined;
    }
    else {
      return request.getAuthDetails().scopedUsers[scope].user= undefined;
    }
  };

  var defaultGetPasswordForUser= function(username, password, callback) {
    callback( new Error("No mechanism specified for retrieving user passwords.") );
  };

  function createAuthDetails(req) {
    var auth= {};
    req._connect_auth= auth;
    if( req.session ) {
      req.session.auth= auth;
    }
  };

  var server=  connect.createServer(
    function auth(req, res, next) {
      // Setup the utility methods
      req.authenticate = function(strategy, opts, callback) {
        var strategy, opts, callback;
        var scope;

        //ughhh pull this rubbish somewhere tidy...
        if( strategy && opts && callback ) {
          var type= typeof strategy;
          if(  strategy.constructor != Array ) {
            strategy= [strategy]
          }
          scope= opts.scope;
        }
        else if( strategy && opts ) {
          callback= opts;
          var type= typeof strategy;
          if( strategy.constructor == Array ) {
            // do nothing
          }
          else if( type == 'string' ) {
            strategy= [strategy];
          }
          else if( type == 'object') {
            scope= strategy.scope
            strategy= undefined;
          }
        }
        else if( strategy ) {
          callback= strategy;
          strategy= undefined;
          //TODO: DEFINE DEFAULT STRATEGY(IES)
        }
        if( isAuthenticated(req, scope) ) callback(null, true);
        else strategyExecutor.authenticate(strategy, scope, req, res, function(error, executionResult){
          if(error) callback(error); // I really wish the plugins code logged errors..
          else {
            if( !executionResult.user ) callback(null,false)
            else {
              if( scope === undefined) {
               req.getAuthDetails().user= executionResult.user;
              }
              else {
                req.getAuthDetails().scopedUsers[scope].user=  executionResult.user;
              }
              callback(null, true)
            }
          }
        });
      };
      req.getAuthDetails= function() {
        return req._connect_auth
      };
      req.isAuthenticated= function(scope) {
        return isAuthenticated( req, scope );
      };
      req.isUnAuthenticated= function(scope) {
        return !isAuthenticated( req, scope );
      };
      req.logout= function(scope) {
        if( scope === undefined) {
         delete req.getAuthDetails().user;
         req.getAuthDetails().scopedUsers= {};
        }
        else {
          delete req.getAuthDetails().scopedUsers[scope].user;
        }
      }

      // If there is a session middleware, use it.
      if( req.session && req.session.auth ) {
        req._connect_auth= req.session.auth;
      }
      else {
        // Create the auth holder if needed.
        if( ! req.getAuthDetails() ) {
          createAuthDetails(req);
        }
      }
      next();
  });

  // Some strategies require routes to be defined, so give them a chance to do so.
  for(var i=0;i< strategies.length; i++ ) {
    if( strategies[i].setupRoutes ) {
      strategies[i].setupRoutes(server);
    }
  }

  var strategyExecutor = new StrategyExecutor( strategies )

  return server;
};

/**
 * Auto-load bundled strategies with getters.
 */
var STRATEGY_EXCLUSIONS= {"base.js" : true,
                          "base64.js" : true};

function augmentAuthWithStrategy(filename, path) {
  if (/\.js$/.test(filename) && !STRATEGY_EXCLUSIONS[filename] && filename[0] != '_') {
      var name = filename.substr(0, filename.lastIndexOf('.'));
      var camelCaseName= name.charAt(0).toUpperCase() + name.substr(1).toLowerCase();
      Object.defineProperty(Auth, camelCaseName, {
        get: function() {
          return require('./' + path+ '/' + name);
        },
        enumerable:true});
  }
}
//TODO: Meh could make this recurse neatly over directories, but I'm lazy.
fs.readdirSync(__dirname + '/auth.strategies').forEach(function(filename){
  augmentAuthWithStrategy(filename, '/auth.strategies')
});
fs.readdirSync(__dirname + '/auth.strategies/http').forEach(function(filename){
  augmentAuthWithStrategy(filename, '/auth.strategies/http')
});
fs.readdirSync(__dirname + '/auth.strategies/oauth').forEach(function(filename){
  augmentAuthWithStrategy(filename, '/auth.strategies/oauth')
});<commit_msg>fixed a typo in auth.js<commit_after>/*!
 * Copyright(c) 2010 Ciaran Jessup <ciaranj@gmail.com>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */
var connect= require('connect');
var fs= require('fs');
var StrategyExecutor= require('./strategyExecutor');

/**
 * Construct the authentication middleware.
 * Construction can take 2 forms:
 *    auth(<Strategy>()/[<Strategy>()])  -  A single configured strategy, or array of strategies.
 *    auth({strategies:<Strategy>()/[<Strategy>()...]})   - More powerful variant that allows for passing in other configuration options, none yet defined.
 */
Auth= module.exports = function(optionsOrStrategy) {

  var strategies;
  var options;
  if( !optionsOrStrategy ) throw new Error("You must specify at least one strategy to use the authentication middleware, even if it is anonymous.");
  // Constructor form 1
  if(  Array.isArray(optionsOrStrategy) || ( optionsOrStrategy.authenticate !== undefined  &&
      optionsOrStrategy.strategies === undefined  ) ) {
    strategies= Array.isArray(optionsOrStrategy) ? optionsOrStrategy : [optionsOrStrategy];
    options= {};
  }
  else {
    options= optionsOrStrategy
    strategies= Array.isArray(optionsOrStrategy.strategies) ? optionsOrStrategy.strategies : [optionsOrStrategy.strategies];
  }
  var isAuthenticated = function( request, scope ) {
    if( scope === undefined ) {
      return request.getAuthDetails().user !== undefined;
    }
    else {
      return request.getAuthDetails().scopedUsers[scope].user !== undefined;
    }
  };

  var defaultGetPasswordForUser= function(username, password, callback) {
    callback( new Error("No mechanism specified for retrieving user passwords.") );
  };

  function createAuthDetails(req) {
    var auth= {};
    req._connect_auth= auth;
    if( req.session ) {
      req.session.auth= auth;
    }
  };

  var server=  connect.createServer(
    function auth(req, res, next) {
      // Setup the utility methods
      req.authenticate = function(strategy, opts, callback) {
        var strategy, opts, callback;
        var scope;

        //ughhh pull this rubbish somewhere tidy...
        if( strategy && opts && callback ) {
          var type= typeof strategy;
          if(  strategy.constructor != Array ) {
            strategy= [strategy]
          }
          scope= opts.scope;
        }
        else if( strategy && opts ) {
          callback= opts;
          var type= typeof strategy;
          if( strategy.constructor == Array ) {
            // do nothing
          }
          else if( type == 'string' ) {
            strategy= [strategy];
          }
          else if( type == 'object') {
            scope= strategy.scope
            strategy= undefined;
          }
        }
        else if( strategy ) {
          callback= strategy;
          strategy= undefined;
          //TODO: DEFINE DEFAULT STRATEGY(IES)
        }
        if( isAuthenticated(req, scope) ) callback(null, true);
        else strategyExecutor.authenticate(strategy, scope, req, res, function(error, executionResult){
          if(error) callback(error); // I really wish the plugins code logged errors..
          else {
            if( !executionResult.user ) callback(null,false)
            else {
              if( scope === undefined) {
               req.getAuthDetails().user= executionResult.user;
              }
              else {
                req.getAuthDetails().scopedUsers[scope].user=  executionResult.user;
              }
              callback(null, true)
            }
          }
        });
      };
      req.getAuthDetails= function() {
        return req._connect_auth
      };
      req.isAuthenticated= function(scope) {
        return isAuthenticated( req, scope );
      };
      req.isUnAuthenticated= function(scope) {
        return !isAuthenticated( req, scope );
      };
      req.logout= function(scope) {
        if( scope === undefined) {
         delete req.getAuthDetails().user;
         req.getAuthDetails().scopedUsers= {};
        }
        else {
          delete req.getAuthDetails().scopedUsers[scope].user;
        }
      }

      // If there is a session middleware, use it.
      if( req.session && req.session.auth ) {
        req._connect_auth= req.session.auth;
      }
      else {
        // Create the auth holder if needed.
        if( ! req.getAuthDetails() ) {
          createAuthDetails(req);
        }
      }
      next();
  });

  // Some strategies require routes to be defined, so give them a chance to do so.
  for(var i=0;i< strategies.length; i++ ) {
    if( strategies[i].setupRoutes ) {
      strategies[i].setupRoutes(server);
    }
  }

  var strategyExecutor = new StrategyExecutor( strategies )

  return server;
};

/**
 * Auto-load bundled strategies with getters.
 */
var STRATEGY_EXCLUSIONS= {"base.js" : true,
                          "base64.js" : true};

function augmentAuthWithStrategy(filename, path) {
  if (/\.js$/.test(filename) && !STRATEGY_EXCLUSIONS[filename] && filename[0] != '_') {
      var name = filename.substr(0, filename.lastIndexOf('.'));
      var camelCaseName= name.charAt(0).toUpperCase() + name.substr(1).toLowerCase();
      Object.defineProperty(Auth, camelCaseName, {
        get: function() {
          return require('./' + path+ '/' + name);
        },
        enumerable:true});
  }
}
//TODO: Meh could make this recurse neatly over directories, but I'm lazy.
fs.readdirSync(__dirname + '/auth.strategies').forEach(function(filename){
  augmentAuthWithStrategy(filename, '/auth.strategies')
});
fs.readdirSync(__dirname + '/auth.strategies/http').forEach(function(filename){
  augmentAuthWithStrategy(filename, '/auth.strategies/http')
});
fs.readdirSync(__dirname + '/auth.strategies/oauth').forEach(function(filename){
  augmentAuthWithStrategy(filename, '/auth.strategies/oauth')
});<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014, Joyent, Inc. All rights reserved.
 *
 * This file defines all the CloudAPI authentication functions,
 * including the following authentication methods:
 *
 * - HTTP Basic Auth (basicAuth): provided just for backwards compatibility
 *   with SDC 6.5 version of CloudAPI. This authentication method is deprecated
 *   for any request with version not being explicitly set to '~6.5'.
 *
 * - HTTP Signature Auth (signatureAuth): both, direct or using token-auth
 *   through 3rd party applications.
 *   Versions 0.9.x and 0.10.x of the http-signature node module are supported.
 *
 * During the authentication process, there are several properties set to the
 * Restify `req` object:
 *
 * - req.caller: Always the sdcPerson object performing the request. It can be
 *   the main account for this request, a sub-user of such account or a
 *   different account, either the same user using a 3rd party application
 *   through token-auth (for example, a web UI), or even an administrative
 *   account performing any action instead of a given user.
 * - req.account: Despite of the value set for `req.caller`, this property will
 *   always be set to the account we're operating as, either if the caller is
 *   the account owner itself, an administrative account or a sub-user.
 * - req.subuser: Only set if the request is being performed by an account
 *   sub-user.
 * - req.username: the login value for `req.caller`, unless on the cases when
 *   a sub-user is performing the request. On such cases, it's set to
 *   `account.login`.
 * - req.subuser: subuser's uuid when present.
 */
var assert = require('assert-plus');
var util = require('util');
// http-signature 0.9.x
var httpSignature = require('./node-http-signature');
// http-signature 0.10.x
var httpSig = require('http-signature');
var restify = require('restify');
var semver = require('semver');
var vasync = require('vasync');

///--- Globals

var BadRequestError = restify.BadRequestError;
var InvalidCredentialsError = restify.InvalidCredentialsError;
var InvalidCreds = restify.InvalidCredentialsError;
var NotAuthorizedError = restify.NotAuthorizedError;
var ResourceNotFoundError = restify.ResourceNotFoundError;

///--- Messages

var DEF_401 = 'You must make authenticated requests to use CloudAPI';
var FORBIDDEN = 'You do not have permission to access %s';
var INVALID_ERR = 'Invalid %s %s';
var INVALID_AUTHZ = 'Invalid authorization header';
var INVALID_KEY = 'Invalid KeyId %s in authorization header';
var INVALID_SIG = 'The signature we calculated does not match the one you sent';
var INVALID_DEV = 'The token provided is not authorized for this application';
var MISSING_CREDS = 'Missing credentials';
var SCHEME_NOT_SUPPORTED = 'The authorization scheme you requested is ' +
    'not supported';
var USER_404 = 'User %s does not exist';

///--- Middleware

function basicAuth(req, res, next) {
    assert.ok(req.log);
    assert.ok(req.sdc);

    var scheme = req.authorization.scheme || '';
    if (scheme.toLowerCase() !== 'basic') {
        return next();
    }

    // basic auth is deprecated on 7.0+
    if (! /6\.5/.test(req.getVersion())) {
        return next(new InvalidCredentialsError(SCHEME_NOT_SUPPORTED));
    }

    var log = req.log;
    var ufds = req.sdc.ufds;

    var user = req.authorization.basic.username;
    var pass = req.authorization.basic.password;
    if (!user || !pass) {
        return next(new InvalidCredentialsError(MISSING_CREDS));
    }

    log.debug('basicAuth: authorization=%j', req.authorization);
    return ufds.authenticate(user, pass, function (err, customer) {
        if (err) {
            log.error({err: err}, 'UFDS.authenticate error');
            if (err.statusCode === 404) {
                return next(new InvalidCredentialsError(USER_404, user));
            }
            return next(new InvalidCredentialsError(DEF_401));
        }
        return ufds.getMetadata(customer, 'cloudapi',
                    function (mderr, metadata) {
            if (mderr || metadata === null ||
                    metadata.require_http_signature !== 'true') {
                req.caller = customer;
                req.username = req.caller.login;
                return next();
            } else {
                return next(new InvalidCredentialsError(SCHEME_NOT_SUPPORTED));
            }
        });
    });
}

function signatureAuth(req, res, next) {
    assert.ok(req.log);
    assert.ok(req.sdc);

    var scheme = req.authorization.scheme || '';
    if (scheme.toLowerCase() !== 'signature') {
        return next();
    }

    var keyId;
    var log = req.log;
    var sig = req.authorization.signature;
    var ufds = req.sdc.ufds;

    try {
        keyId = sig.keyId.split('/');
    } catch (e) {
        log.error('Error parsing authorization header: %s', e.stack);
        return next(new BadRequestError(INVALID_AUTHZ));
    }

    // keyId.length is 4 for main account users and 6 for sub-users,
    // given we do need to identify both, main account, and that the sub-user
    // key may have the same name than the main account key.
    // We'll take advantage of this to difference between main account user
    // and sub-users here
    var v = req.getVersion();
    var account, key, subuser;
    var login, keyID, sublogin;

    if (!keyId || keyId.length !== 4 || keyId[2] !== 'keys') {
        // Sub-users login starting with version 7.2.0:
        if (!(semver.satisfies('7.2.0', v) || semver.ltr('7.2.0', v))) {
            return next(new InvalidCreds(INVALID_KEY, sig.keyId));
        } else {
            if (!keyId || keyId.length !== 6 || keyId[4] !== 'keys') {
                return next(new InvalidCreds(INVALID_KEY, sig.keyId));
            } else {
                sublogin = keyId[3];
                keyID = keyId[5];
            }
        }
    } else {
        keyID = keyId[3];
    }

    login = keyId[1];

    function getAccount(_, cb) {
        ufds.getUser(login, function (err, u) {
            if (err) {
                log.trace({err: err}, 'UFDS.getUser error');
                return cb(new InvalidCreds(INVALID_ERR, 'account', login));
            }
            account = u;
            return cb(null);
        });
    }

    function getKey(_, cb) {
        var a = (subuser) ? account.uuid : null;
        var u = (subuser) ? subuser : account;
        ufds.getKey(u, keyID, a, function (err, k) {
            if (err) {
                log.trace({err: err}, 'UFDS.getKey error');
                return cb(new InvalidCreds(INVALID_ERR, 'key', keyID));
            }
            key = k;
            return cb(null);
        });
    }

    function getSubUser(_, cb) {
        ufds.getUser(sublogin, account.uuid, function (err, u) {
            if (err) {
                log.trace({err: err}, 'UFDS.getUser error');
                return cb(new InvalidCreds(INVALID_ERR, 'account', sublogin));
            }
            subuser = u;
            return cb(null);
        });
    }

    function verifySignature(_, cb) {
        log.debug('authSig using key %s => %s', sig.keyId, key.pkcs);
        var signatureVerified = false;

        try {
            // Attempt using http-signature 0.9.x first
            signatureVerified =
                httpSignature.verifySignature(sig, key.pkcs);
        } catch (e) {
            // On execption, attempt using http-signature 0.10.x:
            try {
                signatureVerified = httpSig.verifySignature(sig, key.pkcs);
            } catch (e2) {
                log.error('Error calling http_signature: ' + e.stack);
                return cb(new InvalidCreds(INVALID_SIG));
            }
        }

        if (!signatureVerified) {
            log.debug('authSig::verify FAIL: %j, key=%s',
                            sig, key.standard);
            return cb(new InvalidCreds(INVALID_SIG));
        }
        return cb(null);
    }

    var funcs = [getAccount];

    if (sublogin) {
        funcs.push(getSubUser);
    }

    return vasync.pipeline({
        funcs: funcs.concat([getKey, verifySignature])
    }, function (err, results) {
        if (err) {
            return next(err);
        }

        req.caller = (subuser) ? subuser : account;
        if (subuser) {
            req.subuser = subuser.uuid;
            req.account = account;
        }
        req.username = account.login;
        return next();
    });
}

function tokenAuth(req, res, next) {
    assert.ok(req.log);
    assert.ok(req.sdc);

    var scheme = req.authorization.scheme || '';
    if (scheme.toLowerCase() !== 'signature') {
        return next();
    }

    var log = req.log;
    var sig = req.authorization.signature;

    if (!req.header('X-Auth-Token')) {
        return next();
    }

    try {
        var token = JSON.parse(req.header('X-Auth-Token'));
        return req.sdc.keyapi.detoken(token,
                function (clienterr, clientobj) {
                if (clienterr) {
                    log.error({err: clienterr}, 'keyapi error');
                    return next(new InvalidCredentialsError(
                        INVALID_DEV));
                }
                if (clientobj.expires) {
                    var expires = new Date(clientobj.expires);
                    var now = new Date();
                    if (now > expires) {
                        return next(new InvalidCredentialsError(
                        INVALID_DEV));
                    }
                }
                if (clientobj.devkeyId !== sig.keyId) {
                    return next(new InvalidCredentialsError(
                            INVALID_DEV));
                }
                if (!clientobj.permissions.cloudapi) {
                    return next(new InvalidCredentialsError(
                            INVALID_DEV));
                }
                var perms = clientobj.permissions.cloudapi;
                var authorized = false;
                var i;
                for (i = 0; i < perms.length; i += 1) {
                    var currperm = perms[i].split('/*');
                    if (currperm[0] === req.url) {
                        authorized = true;
                        break;
                    }
                    if (req.url.search(currperm[0]) === 0) {
                        if (currperm.length === 2 &&
                            req.url.charAt(currperm[0].length) ===
                            '/') {
                            authorized = true;
                            break;
                        }
                    }
                }

                if (authorized === true) {
                    req.caller = clientobj.account;
                    req.username = clientobj.account.login;
                    return next();
                } else {
                    return next(new InvalidCredentialsError(
                        INVALID_DEV));
                }
            });
    } catch (e) {
        log.error('Error calling keyapi: ' + e.stack);
        return next(new InvalidCredentialsError(INVALID_SIG));
    }

}

function assertAuthenticated(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    if (typeof (req.caller) !== 'object') {
        return next(new InvalidCredentialsError(DEF_401));
    }

    return next();
}

function loadAccount(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    assert.ok(req.sdc);

    var log = req.log;
    var ufds = req.sdc.ufds;

    if (req.params.account === 'my') {
        req.params.account = req.caller.login;
    }
    req.params.account = decodeURIComponent(req.params.account);

    if (req.subuser) {
        // Account already set during signature auth, no need to re-do:
        log.debug('account %j already loaded', req.account);
        return next();
    }

    // PUBAPI-780: If request is made by the user itself, using no token-auth
    // we can safely skip account re-loading.
    if (req.username === req.params.account && !req.header('X-Auth-Token')) {
        log.debug('account %j already loaded', req.caller);
        req.account = req.caller;
        return next();
    }

    return ufds.getUser(req.params.account, function (err, customer) {
        if (err) {
            log.error({err: err}, 'UFDS.getUser error');
            return next(err);
        }

        log.debug('loaded account %j', customer);
        req.account = customer;
        return next();
    });
}

// PUBAPI-834: sdcAccountRole/policy authorization for sdcAccountUsers
// takes place here.
function authorize(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    assert.ok(req.account);
    assert.ok(req.caller);

    var account = req.account;
    var caller = req.caller;

    if ((account.uuid !== caller.uuid) && !caller.isAdmin()) {
        if (req.subuser && req.accountMgmt) {
            return req.caller.defaultRoles(function (err, activeRoles) {
                if (err) {
                    return next(
                        new NotAuthorizedError(FORBIDDEN, account.login));
                }
                return req.sdc.mahi.getUser(req.caller.login,
                        req.account.login, function (err2, info) {

                    if (err2) {
                        return next(
                            new NotAuthorizedError(FORBIDDEN, account.login));
                    }

                    var authOpts = {
                        typeTable: req.config.apertureConfig.typeTable,
                        principal: info,
                        action: req.routename,
                        resource: {
                            key: req.path()
                        },
                        conditions: {
                            date: new Date(req._time),
                            day: new Date(req._time),
                            time: new Date(req._time),
                            'user-agent': req.headers['user-agent'],
                            sourceip: req.connection._xff,
                            method: req.method.toLowerCase(),
                            activeRoles: activeRoles.map(function (r) {
                                return (r.uuid);
                            })
                        }
                    };

                    // PENDING: Need to review if we can go ahead with just a
                    // single IP argument or may need to difference between IPs
                    // by nic tag (private, public, ...).
                    if (req.machine && req.machine.ips) {
                        authOpts.conditions.ip = req.machine.ips;
                    }
                    // It is important to document that in order to use tags in
                    // policy rules, the "::string" type name must be appended
                    // to the tag name: `tagName::string=tagValue`:
                    if (req.machine && req.machine.tags) {
                        Object.keys(req.machine.tags).forEach(function (t) {
                            authOpts.conditions[t] = req.machine.tags[t];
                        });
                    }

                    if (req.resource.roles && req.resource.roles.length) {
                        authOpts.resource.roles =
                            req.resource.roles.map(function (r) {
                            return (r.uuid);
                        });
                    } else {
                        authOpts.resource.roles = [];
                    }

                    return req.sdc.mahi.getAccount(req.account.login,
                            function (er, owner) {
                        if (er) {
                            return next(
                                new NotAuthorizedError(FORBIDDEN,
                                    account.login));
                        }

                        authOpts.resource.owner = owner;

                        var authorized;
                        try {
                            authorized = req.sdc.mahi.authorize(authOpts);
                        } catch (e) {
                            return next(
                                new NotAuthorizedError(FORBIDDEN,
                                    authOpts.resource.key + ' (' +
                                    authOpts.action + ')'));
                        }
                        // In theory mahi should raise an exception if the user
                        // is not authorized. Just in case:
                        if (!authorized) {
                            return next(
                                new NotAuthorizedError(FORBIDDEN,
                                    authOpts.resource.key + ' (' +
                                    authOpts.action + ')'));
                        }
                        // We may need this for later use if we are creating
                        // new resources and the sub-user didn't specify any
                        // role.tag:
                        req.activeRoles = activeRoles;
                        return next();
                    });
                });
            });
        } else {
            return next(new NotAuthorizedError(FORBIDDEN, account.login));
        }
    } else {
        return next();
    }
}

///--- Exports

module.exports = {
    basicAuth: basicAuth,
    signatureAuth: signatureAuth,
    tokenAuth: tokenAuth,
    assertAuthenticated: assertAuthenticated,
    loadAccount: loadAccount,
    authorize: authorize
};
<commit_msg>PUBAPI-901: Prefix machine tags with "tag_" when adding to aperture context (in order to prevent collision with our default context variables)<commit_after>/*
 * Copyright (c) 2014, Joyent, Inc. All rights reserved.
 *
 * This file defines all the CloudAPI authentication functions,
 * including the following authentication methods:
 *
 * - HTTP Basic Auth (basicAuth): provided just for backwards compatibility
 *   with SDC 6.5 version of CloudAPI. This authentication method is deprecated
 *   for any request with version not being explicitly set to '~6.5'.
 *
 * - HTTP Signature Auth (signatureAuth): both, direct or using token-auth
 *   through 3rd party applications.
 *   Versions 0.9.x and 0.10.x of the http-signature node module are supported.
 *
 * During the authentication process, there are several properties set to the
 * Restify `req` object:
 *
 * - req.caller: Always the sdcPerson object performing the request. It can be
 *   the main account for this request, a sub-user of such account or a
 *   different account, either the same user using a 3rd party application
 *   through token-auth (for example, a web UI), or even an administrative
 *   account performing any action instead of a given user.
 * - req.account: Despite of the value set for `req.caller`, this property will
 *   always be set to the account we're operating as, either if the caller is
 *   the account owner itself, an administrative account or a sub-user.
 * - req.subuser: Only set if the request is being performed by an account
 *   sub-user.
 * - req.username: the login value for `req.caller`, unless on the cases when
 *   a sub-user is performing the request. On such cases, it's set to
 *   `account.login`.
 * - req.subuser: subuser's uuid when present.
 */
var assert = require('assert-plus');
var util = require('util');
// http-signature 0.9.x
var httpSignature = require('./node-http-signature');
// http-signature 0.10.x
var httpSig = require('http-signature');
var restify = require('restify');
var semver = require('semver');
var vasync = require('vasync');

///--- Globals

var BadRequestError = restify.BadRequestError;
var InvalidCredentialsError = restify.InvalidCredentialsError;
var InvalidCreds = restify.InvalidCredentialsError;
var NotAuthorizedError = restify.NotAuthorizedError;
var ResourceNotFoundError = restify.ResourceNotFoundError;

///--- Messages

var DEF_401 = 'You must make authenticated requests to use CloudAPI';
var FORBIDDEN = 'You do not have permission to access %s';
var INVALID_ERR = 'Invalid %s %s';
var INVALID_AUTHZ = 'Invalid authorization header';
var INVALID_KEY = 'Invalid KeyId %s in authorization header';
var INVALID_SIG = 'The signature we calculated does not match the one you sent';
var INVALID_DEV = 'The token provided is not authorized for this application';
var MISSING_CREDS = 'Missing credentials';
var SCHEME_NOT_SUPPORTED = 'The authorization scheme you requested is ' +
    'not supported';
var USER_404 = 'User %s does not exist';

///--- Middleware

function basicAuth(req, res, next) {
    assert.ok(req.log);
    assert.ok(req.sdc);

    var scheme = req.authorization.scheme || '';
    if (scheme.toLowerCase() !== 'basic') {
        return next();
    }

    // basic auth is deprecated on 7.0+
    if (! /6\.5/.test(req.getVersion())) {
        return next(new InvalidCredentialsError(SCHEME_NOT_SUPPORTED));
    }

    var log = req.log;
    var ufds = req.sdc.ufds;

    var user = req.authorization.basic.username;
    var pass = req.authorization.basic.password;
    if (!user || !pass) {
        return next(new InvalidCredentialsError(MISSING_CREDS));
    }

    log.debug('basicAuth: authorization=%j', req.authorization);
    return ufds.authenticate(user, pass, function (err, customer) {
        if (err) {
            log.error({err: err}, 'UFDS.authenticate error');
            if (err.statusCode === 404) {
                return next(new InvalidCredentialsError(USER_404, user));
            }
            return next(new InvalidCredentialsError(DEF_401));
        }
        return ufds.getMetadata(customer, 'cloudapi',
                    function (mderr, metadata) {
            if (mderr || metadata === null ||
                    metadata.require_http_signature !== 'true') {
                req.caller = customer;
                req.username = req.caller.login;
                return next();
            } else {
                return next(new InvalidCredentialsError(SCHEME_NOT_SUPPORTED));
            }
        });
    });
}

function signatureAuth(req, res, next) {
    assert.ok(req.log);
    assert.ok(req.sdc);

    var scheme = req.authorization.scheme || '';
    if (scheme.toLowerCase() !== 'signature') {
        return next();
    }

    var keyId;
    var log = req.log;
    var sig = req.authorization.signature;
    var ufds = req.sdc.ufds;

    try {
        keyId = sig.keyId.split('/');
    } catch (e) {
        log.error('Error parsing authorization header: %s', e.stack);
        return next(new BadRequestError(INVALID_AUTHZ));
    }

    // keyId.length is 4 for main account users and 6 for sub-users,
    // given we do need to identify both, main account, and that the sub-user
    // key may have the same name than the main account key.
    // We'll take advantage of this to difference between main account user
    // and sub-users here
    var v = req.getVersion();
    var account, key, subuser;
    var login, keyID, sublogin;

    if (!keyId || keyId.length !== 4 || keyId[2] !== 'keys') {
        // Sub-users login starting with version 7.2.0:
        if (!(semver.satisfies('7.2.0', v) || semver.ltr('7.2.0', v))) {
            return next(new InvalidCreds(INVALID_KEY, sig.keyId));
        } else {
            if (!keyId || keyId.length !== 6 || keyId[4] !== 'keys') {
                return next(new InvalidCreds(INVALID_KEY, sig.keyId));
            } else {
                sublogin = keyId[3];
                keyID = keyId[5];
            }
        }
    } else {
        keyID = keyId[3];
    }

    login = keyId[1];

    function getAccount(_, cb) {
        ufds.getUser(login, function (err, u) {
            if (err) {
                log.trace({err: err}, 'UFDS.getUser error');
                return cb(new InvalidCreds(INVALID_ERR, 'account', login));
            }
            account = u;
            return cb(null);
        });
    }

    function getKey(_, cb) {
        var a = (subuser) ? account.uuid : null;
        var u = (subuser) ? subuser : account;
        ufds.getKey(u, keyID, a, function (err, k) {
            if (err) {
                log.trace({err: err}, 'UFDS.getKey error');
                return cb(new InvalidCreds(INVALID_ERR, 'key', keyID));
            }
            key = k;
            return cb(null);
        });
    }

    function getSubUser(_, cb) {
        ufds.getUser(sublogin, account.uuid, function (err, u) {
            if (err) {
                log.trace({err: err}, 'UFDS.getUser error');
                return cb(new InvalidCreds(INVALID_ERR, 'account', sublogin));
            }
            subuser = u;
            return cb(null);
        });
    }

    function verifySignature(_, cb) {
        log.debug('authSig using key %s => %s', sig.keyId, key.pkcs);
        var signatureVerified = false;

        try {
            // Attempt using http-signature 0.9.x first
            signatureVerified =
                httpSignature.verifySignature(sig, key.pkcs);
        } catch (e) {
            // On execption, attempt using http-signature 0.10.x:
            try {
                signatureVerified = httpSig.verifySignature(sig, key.pkcs);
            } catch (e2) {
                log.error('Error calling http_signature: ' + e.stack);
                return cb(new InvalidCreds(INVALID_SIG));
            }
        }

        if (!signatureVerified) {
            log.debug('authSig::verify FAIL: %j, key=%s',
                            sig, key.standard);
            return cb(new InvalidCreds(INVALID_SIG));
        }
        return cb(null);
    }

    var funcs = [getAccount];

    if (sublogin) {
        funcs.push(getSubUser);
    }

    return vasync.pipeline({
        funcs: funcs.concat([getKey, verifySignature])
    }, function (err, results) {
        if (err) {
            return next(err);
        }

        req.caller = (subuser) ? subuser : account;
        if (subuser) {
            req.subuser = subuser.uuid;
            req.account = account;
        }
        req.username = account.login;
        return next();
    });
}

function tokenAuth(req, res, next) {
    assert.ok(req.log);
    assert.ok(req.sdc);

    var scheme = req.authorization.scheme || '';
    if (scheme.toLowerCase() !== 'signature') {
        return next();
    }

    var log = req.log;
    var sig = req.authorization.signature;

    if (!req.header('X-Auth-Token')) {
        return next();
    }

    try {
        var token = JSON.parse(req.header('X-Auth-Token'));
        return req.sdc.keyapi.detoken(token,
                function (clienterr, clientobj) {
                if (clienterr) {
                    log.error({err: clienterr}, 'keyapi error');
                    return next(new InvalidCredentialsError(
                        INVALID_DEV));
                }
                if (clientobj.expires) {
                    var expires = new Date(clientobj.expires);
                    var now = new Date();
                    if (now > expires) {
                        return next(new InvalidCredentialsError(
                        INVALID_DEV));
                    }
                }
                if (clientobj.devkeyId !== sig.keyId) {
                    return next(new InvalidCredentialsError(
                            INVALID_DEV));
                }
                if (!clientobj.permissions.cloudapi) {
                    return next(new InvalidCredentialsError(
                            INVALID_DEV));
                }
                var perms = clientobj.permissions.cloudapi;
                var authorized = false;
                var i;
                for (i = 0; i < perms.length; i += 1) {
                    var currperm = perms[i].split('/*');
                    if (currperm[0] === req.url) {
                        authorized = true;
                        break;
                    }
                    if (req.url.search(currperm[0]) === 0) {
                        if (currperm.length === 2 &&
                            req.url.charAt(currperm[0].length) ===
                            '/') {
                            authorized = true;
                            break;
                        }
                    }
                }

                if (authorized === true) {
                    req.caller = clientobj.account;
                    req.username = clientobj.account.login;
                    return next();
                } else {
                    return next(new InvalidCredentialsError(
                        INVALID_DEV));
                }
            });
    } catch (e) {
        log.error('Error calling keyapi: ' + e.stack);
        return next(new InvalidCredentialsError(INVALID_SIG));
    }

}

function assertAuthenticated(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    if (typeof (req.caller) !== 'object') {
        return next(new InvalidCredentialsError(DEF_401));
    }

    return next();
}

function loadAccount(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    assert.ok(req.sdc);

    var log = req.log;
    var ufds = req.sdc.ufds;

    if (req.params.account === 'my') {
        req.params.account = req.caller.login;
    }
    req.params.account = decodeURIComponent(req.params.account);

    if (req.subuser) {
        // Account already set during signature auth, no need to re-do:
        log.debug('account %j already loaded', req.account);
        return next();
    }

    // PUBAPI-780: If request is made by the user itself, using no token-auth
    // we can safely skip account re-loading.
    if (req.username === req.params.account && !req.header('X-Auth-Token')) {
        log.debug('account %j already loaded', req.caller);
        req.account = req.caller;
        return next();
    }

    return ufds.getUser(req.params.account, function (err, customer) {
        if (err) {
            log.error({err: err}, 'UFDS.getUser error');
            return next(err);
        }

        log.debug('loaded account %j', customer);
        req.account = customer;
        return next();
    });
}

// PUBAPI-834: sdcAccountRole/policy authorization for sdcAccountUsers
// takes place here.
function authorize(req, res, next) {
    if (req.url === '/--ping') {
        return next();
    }

    assert.ok(req.account);
    assert.ok(req.caller);

    var account = req.account;
    var caller = req.caller;

    if ((account.uuid !== caller.uuid) && !caller.isAdmin()) {
        if (req.subuser && req.accountMgmt) {
            return req.caller.defaultRoles(function (err, activeRoles) {
                if (err) {
                    return next(
                        new NotAuthorizedError(FORBIDDEN, account.login));
                }
                return req.sdc.mahi.getUser(req.caller.login,
                        req.account.login, function (err2, info) {

                    if (err2) {
                        return next(
                            new NotAuthorizedError(FORBIDDEN, account.login));
                    }

                    var authOpts = {
                        typeTable: req.config.apertureConfig.typeTable,
                        principal: info,
                        action: req.routename,
                        resource: {
                            key: req.path()
                        },
                        conditions: {
                            date: new Date(req._time),
                            day: new Date(req._time),
                            time: new Date(req._time),
                            'user-agent': req.headers['user-agent'],
                            sourceip: req.connection._xff,
                            method: req.method.toLowerCase(),
                            activeRoles: activeRoles.map(function (r) {
                                return (r.uuid);
                            })
                        }
                    };

                    // PENDING: Need to review if we can go ahead with just a
                    // single IP argument or may need to difference between IPs
                    // by nic tag (private, public, ...).
                    if (req.machine && req.machine.ips) {
                        authOpts.conditions.ip = req.machine.ips;
                    }
                    // It is important to document that in order to use tags in
                    // policy rules, the "::string" type name must be appended
                    // to the tag name: `tag_$tagName::string=$tagValue`:
                    if (req.machine && req.machine.tags) {
                        Object.keys(req.machine.tags).forEach(function (t) {
                            authOpts.conditions['tag_' + t] =
                                req.machine.tags[t];
                        });
                    }

                    if (req.resource.roles && req.resource.roles.length) {
                        authOpts.resource.roles =
                            req.resource.roles.map(function (r) {
                            return (r.uuid);
                        });
                    } else {
                        authOpts.resource.roles = [];
                    }

                    return req.sdc.mahi.getAccount(req.account.login,
                            function (er, owner) {
                        if (er) {
                            return next(
                                new NotAuthorizedError(FORBIDDEN,
                                    account.login));
                        }

                        authOpts.resource.owner = owner;

                        var authorized;
                        try {
                            authorized = req.sdc.mahi.authorize(authOpts);
                        } catch (e) {
                            return next(
                                new NotAuthorizedError(FORBIDDEN,
                                    authOpts.resource.key + ' (' +
                                    authOpts.action + ')'));
                        }
                        // In theory mahi should raise an exception if the user
                        // is not authorized. Just in case:
                        if (!authorized) {
                            return next(
                                new NotAuthorizedError(FORBIDDEN,
                                    authOpts.resource.key + ' (' +
                                    authOpts.action + ')'));
                        }
                        // We may need this for later use if we are creating
                        // new resources and the sub-user didn't specify any
                        // role.tag:
                        req.activeRoles = activeRoles;
                        return next();
                    });
                });
            });
        } else {
            return next(new NotAuthorizedError(FORBIDDEN, account.login));
        }
    } else {
        return next();
    }
}

///--- Exports

module.exports = {
    basicAuth: basicAuth,
    signatureAuth: signatureAuth,
    tokenAuth: tokenAuth,
    assertAuthenticated: assertAuthenticated,
    loadAccount: loadAccount,
    authorize: authorize
};
<|endoftext|>


<commit_before>// authentication and authorization functions
var Client = require('cas.js');
var url = require('url');
var ad = require('../config/ad.json');

var ldapClient = require('../lib/ldap-client');

var mongoose = require('mongoose');
var User = mongoose.model('User');

var cas = new Client({
  base_url: 'https://liud-dev.nscl.msu.edu/cas',
  service: 'http://localhost:3001',
  version: 1.0
});

module.exports = {
  ensureAuthenticated: ensureAuthenticated,
  verifyRole: verifyRole
};

function ensureAuthenticated(req, res, next) {
  var ticketUrl = url.parse(req.url, true);
  if (req.session.userid) {
    // console.log(req.session);
    if (req.query.ticket) {
      // remove the ticket query param
      delete ticketUrl.query.ticket;
      return res.redirect(301, url.format({
        pathname: ticketUrl.pathname,
        query: ticketUrl.query
      }));
    } else {
      return next();
    }
  } else if (req.query.ticket) {
    cas.validate(req.query.ticket, function(err, status, userid) {
      if (err) {
        console.error(err.message);
        return res.send(401, err.message);
      } else {
        if (status) {
          req.session.userid = userid;
          User.findOne({
            id: userid
          }).lean().exec(function(err, user) {
            if (err) {
              console.error(err.msg);
              // still let it go without create the user?
              req.session.roles = [];
              req.session.username = 'unknown';
              return next();
            } else {
              if (user) {
                req.session.roles = user.roles;
                req.session.username = user.name;
                User.findByIdAndUpdate(user._id, {
                  lastVisitedOn: Date.now()
                }).lean().exec(function(err, update) {
                  if (err) {
                    console.err(err.message);
                  }
                });
                if (req.session.landing) {
                  return res.redirect(req.session.landing);
                } else {
                  return next();
                }
              } else {
                // create a new user
                var searchFilter = ad.searchFilter.replace('_id', userid);
                var opts = {
                  filter: searchFilter,
                  attributes: ad.objAttributes,
                  scope: 'sub'
                };
                ldapClient.search(ad.searchBase, opts, false, function(err, result) {
                  if (err) {
                    console.err(err.name + ' : ' + err.message);
                    return res.send(500, 'something wrong with ad');
                  }
                  if (result.length === 0) {
                    console.warn('cannot find ' + userid);
                    return res.send(500, userid + ' is not found!');
                  }
                  if (result.length > 1) {
                    return res.send(500, userid + ' is not unique!');
                  }

                  var first = new User({
                    id: userid,
                    name: result[0].displayName,
                    email: result[0].mail,
                    office: result[0].physicalDeliveryOfficeName,
                    phone: result[0].telephoneNumber,
                    mobile: result[0].mobile,
                    roles: [],
                    lastVisitedOn: Date.now()
                  });

                  first.save(function(err, newUser) {
                    if (err) {
                      console.err(err.msg);
                    }
                    req.session.roles = [];
                    console.info('A new user logs in: ' + newUser.name);
                    req.session.username = newUser.name;
                    return next();
                  });

                });

              }
            }
          });
        } else {
          console.error('CAS reject this ticket');
          return res.redirect(cas.service);
        }

      }
    });
  } else {
    req.session.landing = req.url;
    return res.redirect('https://' + cas.hostname + cas.base_path + '/login?service=' + encodeURIComponent(cas.service));
  }
}

function verifyRole(role) {
  return function(req, res, next) {
    // console.log(req.session);
    if (req.session.roles) {
      if (req.session.roles.indexOf(role) > -1) {
        return next();
      } else {
        return res.send(403, "You are not authorized to access this resource. ");
      }
    } else {
      console.log("Cannot find the user's role.");
      return res.send(500, "something wrong for the user's session");
    }
  };
}<commit_msg>update to handle ajax<commit_after>// authentication and authorization functions
var Client = require('cas.js');
var url = require('url');
var ad = require('../config/ad.json');

var ldapClient = require('../lib/ldap-client');

var mongoose = require('mongoose');
var User = mongoose.model('User');

var cas = new Client({
  base_url: 'https://liud-dev.nscl.msu.edu/cas',
  service: 'http://localhost:3001',
  version: 1.0
});

module.exports = {
  ensureAuthenticated: ensureAuthenticated,
  verifyRole: verifyRole
};

function ensureAuthenticated(req, res, next) {
  var ticketUrl = url.parse(req.url, true);
  if (req.session.userid) {
    // console.log(req.session);
    if (req.query.ticket) {
      // remove the ticket query param
      delete ticketUrl.query.ticket;
      return res.redirect(301, url.format({
        pathname: ticketUrl.pathname,
        query: ticketUrl.query
      }));
    } else {
      return next();
    }
  } else if (req.query.ticket) {
    cas.validate(req.query.ticket, function(err, status, userid) {
      if (err) {
        console.error(err.message);
        return res.send(401, err.message);
      } else {
        if (status) {
          req.session.userid = userid;
          User.findOne({
            id: userid
          }).lean().exec(function(err, user) {
            if (err) {
              console.error(err.msg);
              // still let it go without create the user?
              req.session.roles = [];
              req.session.username = 'unknown';
              return next();
            } else {
              if (user) {
                req.session.roles = user.roles;
                req.session.username = user.name;
                User.findByIdAndUpdate(user._id, {
                  lastVisitedOn: Date.now()
                }).lean().exec(function(err, update) {
                  if (err) {
                    console.err(err.message);
                  }
                });
                if (req.session.landing) {
                  return res.redirect(req.session.landing);
                } else {
                  return next();
                }
              } else {
                // create a new user
                var searchFilter = ad.searchFilter.replace('_id', userid);
                var opts = {
                  filter: searchFilter,
                  attributes: ad.objAttributes,
                  scope: 'sub'
                };
                ldapClient.search(ad.searchBase, opts, false, function(err, result) {
                  if (err) {
                    console.err(err.name + ' : ' + err.message);
                    return res.send(500, 'something wrong with ad');
                  }
                  if (result.length === 0) {
                    console.warn('cannot find ' + userid);
                    return res.send(500, userid + ' is not found!');
                  }
                  if (result.length > 1) {
                    return res.send(500, userid + ' is not unique!');
                  }

                  var first = new User({
                    id: userid,
                    name: result[0].displayName,
                    email: result[0].mail,
                    office: result[0].physicalDeliveryOfficeName,
                    phone: result[0].telephoneNumber,
                    mobile: result[0].mobile,
                    roles: [],
                    lastVisitedOn: Date.now()
                  });

                  first.save(function(err, newUser) {
                    if (err) {
                      console.err(err.msg);
                    }
                    req.session.roles = [];
                    console.info('A new user logs in: ' + newUser.name);
                    req.session.username = newUser.name;
                    return next();
                  });

                });

              }
            }
          });
        } else {
          console.error('CAS reject this ticket');
          return res.redirect(cas.service);
        }

      }
    });
  } else {
    // if this is ajax call, then force the browser to refresh to the current page
    if (req.xhr) {
      res.set('WWW-Authenticate', 'CAS realm="' + url.format({protocol: 'http', hostname: ticketUrl.hostname}) + '"');
      return res.send(401);
    } else {
      req.session.landing = req.url;
      return res.redirect('https://' + cas.hostname + cas.base_path + '/login?service=' + encodeURIComponent(cas.service));
    }
  }
}

function verifyRole(role) {
  return function(req, res, next) {
    // console.log(req.session);
    if (req.session.roles) {
      if (req.session.roles.indexOf(role) > -1) {
        return next();
      } else {
        return res.send(403, "You are not authorized to access this resource. ");
      }
    } else {
      console.log("Cannot find the user's role.");
      return res.send(500, "something wrong for the user's session");
    }
  };
}<|endoftext|>