[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/webroot/rsrc/js/core/ -> behavior-refresh-csrf.js (source)

   1  /**
   2   * @provides javelin-behavior-refresh-csrf
   3   * @requires javelin-request
   4   *           javelin-behavior
   5   *           javelin-dom
   6   *           javelin-router
   7   *           javelin-util
   8   *           phabricator-busy
   9   */
  10  
  11  /**
  12   * We cycle CSRF tokens every hour but accept the last 6, which means that if
  13   * you leave a page open for more than 6 hours before submitting it you may hit
  14   * CSRF protection. This is a super confusing workflow which potentially
  15   * discards data, and we can't recover from it by re-issuing a CSRF token
  16   * since that would leave us vulnerable to CSRF attacks.
  17   *
  18   * Our options basically boil down to:
  19   *
  20   *   - Increase the CSRF window to something like 24 hours. This makes the CSRF
  21   *     implementation vaguely less secure and only mitigates the problem.
  22   *   - Code all forms to understand GET, POST and POST-with-invalid-CSRF. This
  23   *     is a huge undertaking and difficult to test properly since it is hard
  24   *     to remember to test the third phantom state.
  25   *   - Use JS to refresh the CSRF token.
  26   *
  27   * Since (1) mitigates rather than solving and (2) is a huge mess, this
  28   * behavior implements (3) and refreshes all the CSRF tokens on the page every
  29   * 55 minutes. This allows forms to remain valid indefinitely.
  30   */
  31  JX.behavior('refresh-csrf', function(config) {
  32  
  33    var current_token = config.current;
  34  
  35    function refresh_csrf() {
  36      new JX.Request('/login/refresh/', function(r) {
  37        current_token = r.token;
  38        var inputs = JX.DOM.scry(document.body, 'input');
  39        for (var ii = 0; ii < inputs.length; ii++) {
  40          if (inputs[ii].name == config.tokenName) {
  41            inputs[ii].value = r.token;
  42          }
  43        }
  44      })
  45      .send();
  46    }
  47  
  48    // Refresh the CSRF tokens every 55 minutes.
  49    setInterval(refresh_csrf, 1000 * 60 * 55);
  50  
  51    // Additionally, add the CSRF token as an HTTP header to every AJAX request.
  52    JX.Request.listen('open', function(r) {
  53      r.getTransport().setRequestHeader(config.header, current_token);
  54    });
  55  
  56    // Does this type of routable show the "Busy" spinner?
  57    var is_busy_type = function(type) {
  58      switch (type) {
  59        case 'workflow':
  60          return true;
  61      }
  62  
  63      return false;
  64    };
  65  
  66    // Does this type of routable show the "Loading" bar?
  67    var is_bar_type = function(type) {
  68      switch (type) {
  69        case 'content':
  70          return true;
  71      }
  72  
  73      return false;
  74    };
  75  
  76  
  77    var queue = {};
  78    var count = 0;
  79    var node;
  80  
  81    // Redraw the loading bar.
  82    var redraw_bar = function() {
  83      // If all requests have completed, hide the bar after a moment.
  84      if (!count) {
  85        if (node) {
  86          node.firstChild.style.width = '100%';
  87          setTimeout(JX.bind(null, JX.DOM.remove, node), 500);
  88        }
  89        node = null;
  90        queue = {};
  91        return;
  92      }
  93  
  94      // If we don't have the bar yet, draw it.
  95      if (!node) {
  96        node = JX.$N('div', {className: 'routing-bar'});
  97        document.body.appendChild(node);
  98        node.appendChild(JX.$N('div', {className: 'routing-progress'}));
  99      }
 100  
 101      // Update the bar progress.
 102      var done = 0;
 103      var total = 0;
 104      for (var k in queue) {
 105        total++;
 106        if (queue[k]) {
 107          done++;
 108        }
 109      }
 110  
 111      node.firstChild.style.width = (100 * (done / total)) + '%';
 112    };
 113  
 114  
 115    // Listen for queued requests.
 116    JX.Router.listen('queue', function(r) {
 117      var type = r.getType();
 118  
 119      if (is_bar_type(type)) {
 120        queue[r.getID()] = false;
 121        count++;
 122        redraw_bar();
 123      }
 124  
 125      if (is_busy_type(r.getType())) {
 126        JX.Busy.start();
 127      }
 128    });
 129  
 130  
 131    // Listen for completed requests.
 132    JX.Router.listen('done', function(r) {
 133      var type = r.getType();
 134  
 135      if (is_bar_type(type)) {
 136        queue[r.getID()] = true;
 137        count--;
 138        redraw_bar();
 139      }
 140  
 141      if (is_busy_type(r.getType())) {
 142        JX.Busy.done();
 143      }
 144    });
 145  
 146  
 147  });


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1