"Fossies" - the Fresh Open Source Software Archive

Member "Atom/resources/app/apm/node_modules/request/node_modules/http-signature/lib/parser.js" (7 Feb 2017, 9772 Bytes) of archive /windows/misc/atom-windows.zip:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Javascript source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2012 Joyent, Inc.  All rights reserved.
    2 
    3 var assert = require('assert-plus');
    4 var util = require('util');
    5 var utils = require('./utils');
    6 
    7 
    8 
    9 ///--- Globals
   10 
   11 var HASH_ALGOS = utils.HASH_ALGOS;
   12 var PK_ALGOS = utils.PK_ALGOS;
   13 var HttpSignatureError = utils.HttpSignatureError;
   14 var InvalidAlgorithmError = utils.InvalidAlgorithmError;
   15 var validateAlgorithm = utils.validateAlgorithm;
   16 
   17 var State = {
   18   New: 0,
   19   Params: 1
   20 };
   21 
   22 var ParamsState = {
   23   Name: 0,
   24   Quote: 1,
   25   Value: 2,
   26   Comma: 3
   27 };
   28 
   29 
   30 ///--- Specific Errors
   31 
   32 
   33 function ExpiredRequestError(message) {
   34   HttpSignatureError.call(this, message, ExpiredRequestError);
   35 }
   36 util.inherits(ExpiredRequestError, HttpSignatureError);
   37 
   38 
   39 function InvalidHeaderError(message) {
   40   HttpSignatureError.call(this, message, InvalidHeaderError);
   41 }
   42 util.inherits(InvalidHeaderError, HttpSignatureError);
   43 
   44 
   45 function InvalidParamsError(message) {
   46   HttpSignatureError.call(this, message, InvalidParamsError);
   47 }
   48 util.inherits(InvalidParamsError, HttpSignatureError);
   49 
   50 
   51 function MissingHeaderError(message) {
   52   HttpSignatureError.call(this, message, MissingHeaderError);
   53 }
   54 util.inherits(MissingHeaderError, HttpSignatureError);
   55 
   56 function StrictParsingError(message) {
   57   HttpSignatureError.call(this, message, StrictParsingError);
   58 }
   59 util.inherits(StrictParsingError, HttpSignatureError);
   60 
   61 ///--- Exported API
   62 
   63 module.exports = {
   64 
   65   /**
   66    * Parses the 'Authorization' header out of an http.ServerRequest object.
   67    *
   68    * Note that this API will fully validate the Authorization header, and throw
   69    * on any error.  It will not however check the signature, or the keyId format
   70    * as those are specific to your environment.  You can use the options object
   71    * to pass in extra constraints.
   72    *
   73    * As a response object you can expect this:
   74    *
   75    *     {
   76    *       "scheme": "Signature",
   77    *       "params": {
   78    *         "keyId": "foo",
   79    *         "algorithm": "rsa-sha256",
   80    *         "headers": [
   81    *           "date" or "x-date",
   82    *           "digest"
   83    *         ],
   84    *         "signature": "base64"
   85    *       },
   86    *       "signingString": "ready to be passed to crypto.verify()"
   87    *     }
   88    *
   89    * @param {Object} request an http.ServerRequest.
   90    * @param {Object} options an optional options object with:
   91    *                   - clockSkew: allowed clock skew in seconds (default 300).
   92    *                   - headers: required header names (def: date or x-date)
   93    *                   - algorithms: algorithms to support (default: all).
   94    *                   - strict: should enforce latest spec parsing
   95    *                             (default: false).
   96    * @return {Object} parsed out object (see above).
   97    * @throws {TypeError} on invalid input.
   98    * @throws {InvalidHeaderError} on an invalid Authorization header error.
   99    * @throws {InvalidParamsError} if the params in the scheme are invalid.
  100    * @throws {MissingHeaderError} if the params indicate a header not present,
  101    *                              either in the request headers from the params,
  102    *                              or not in the params from a required header
  103    *                              in options.
  104    * @throws {StrictParsingError} if old attributes are used in strict parsing
  105    *                              mode.
  106    * @throws {ExpiredRequestError} if the value of date or x-date exceeds skew.
  107    */
  108   parseRequest: function parseRequest(request, options) {
  109     assert.object(request, 'request');
  110     assert.object(request.headers, 'request.headers');
  111     if (options === undefined) {
  112       options = {};
  113     }
  114     if (options.headers === undefined) {
  115       options.headers = [request.headers['x-date'] ? 'x-date' : 'date'];
  116     }
  117     assert.object(options, 'options');
  118     assert.arrayOfString(options.headers, 'options.headers');
  119     assert.optionalNumber(options.clockSkew, 'options.clockSkew');
  120 
  121     if (!request.headers.authorization)
  122       throw new MissingHeaderError('no authorization header present in ' +
  123                                    'the request');
  124 
  125     options.clockSkew = options.clockSkew || 300;
  126 
  127 
  128     var i = 0;
  129     var state = State.New;
  130     var substate = ParamsState.Name;
  131     var tmpName = '';
  132     var tmpValue = '';
  133 
  134     var parsed = {
  135       scheme: '',
  136       params: {},
  137       signingString: '',
  138 
  139       get algorithm() {
  140         return this.params.algorithm.toUpperCase();
  141       },
  142 
  143       get keyId() {
  144         return this.params.keyId;
  145       }
  146     };
  147 
  148     var authz = request.headers.authorization;
  149     for (i = 0; i < authz.length; i++) {
  150       var c = authz.charAt(i);
  151 
  152       switch (Number(state)) {
  153 
  154       case State.New:
  155         if (c !== ' ') parsed.scheme += c;
  156         else state = State.Params;
  157         break;
  158 
  159       case State.Params:
  160         switch (Number(substate)) {
  161 
  162         case ParamsState.Name:
  163           var code = c.charCodeAt(0);
  164           // restricted name of A-Z / a-z
  165           if ((code >= 0x41 && code <= 0x5a) || // A-Z
  166               (code >= 0x61 && code <= 0x7a)) { // a-z
  167             tmpName += c;
  168           } else if (c === '=') {
  169             if (tmpName.length === 0)
  170               throw new InvalidHeaderError('bad param format');
  171             substate = ParamsState.Quote;
  172           } else {
  173             throw new InvalidHeaderError('bad param format');
  174           }
  175           break;
  176 
  177         case ParamsState.Quote:
  178           if (c === '"') {
  179             tmpValue = '';
  180             substate = ParamsState.Value;
  181           } else {
  182             throw new InvalidHeaderError('bad param format');
  183           }
  184           break;
  185 
  186         case ParamsState.Value:
  187           if (c === '"') {
  188             parsed.params[tmpName] = tmpValue;
  189             substate = ParamsState.Comma;
  190           } else {
  191             tmpValue += c;
  192           }
  193           break;
  194 
  195         case ParamsState.Comma:
  196           if (c === ',') {
  197             tmpName = '';
  198             substate = ParamsState.Name;
  199           } else {
  200             throw new InvalidHeaderError('bad param format');
  201           }
  202           break;
  203 
  204         default:
  205           throw new Error('Invalid substate');
  206         }
  207         break;
  208 
  209       default:
  210         throw new Error('Invalid substate');
  211       }
  212 
  213     }
  214 
  215     if (!parsed.params.headers || parsed.params.headers === '') {
  216       if (request.headers['x-date']) {
  217         parsed.params.headers = ['x-date'];
  218       } else {
  219         parsed.params.headers = ['date'];
  220       }
  221     } else {
  222       parsed.params.headers = parsed.params.headers.split(' ');
  223     }
  224 
  225     // Minimally validate the parsed object
  226     if (!parsed.scheme || parsed.scheme !== 'Signature')
  227       throw new InvalidHeaderError('scheme was not "Signature"');
  228 
  229     if (!parsed.params.keyId)
  230       throw new InvalidHeaderError('keyId was not specified');
  231 
  232     if (!parsed.params.algorithm)
  233       throw new InvalidHeaderError('algorithm was not specified');
  234 
  235     if (!parsed.params.signature)
  236       throw new InvalidHeaderError('signature was not specified');
  237 
  238     // Check the algorithm against the official list
  239     parsed.params.algorithm = parsed.params.algorithm.toLowerCase();
  240     try {
  241       validateAlgorithm(parsed.params.algorithm);
  242     } catch (e) {
  243       if (e instanceof InvalidAlgorithmError)
  244         throw (new InvalidParamsError(parsed.params.algorithm + ' is not ' +
  245           'supported'));
  246       else
  247         throw (e);
  248     }
  249 
  250     // Build the signingString
  251     for (i = 0; i < parsed.params.headers.length; i++) {
  252       var h = parsed.params.headers[i].toLowerCase();
  253       parsed.params.headers[i] = h;
  254 
  255       if (h === 'request-line') {
  256         if (!options.strict) {
  257           /*
  258            * We allow headers from the older spec drafts if strict parsing isn't
  259            * specified in options.
  260            */
  261           parsed.signingString +=
  262             request.method + ' ' + request.url + ' HTTP/' + request.httpVersion;
  263         } else {
  264           /* Strict parsing doesn't allow older draft headers. */
  265           throw (new StrictParsingError('request-line is not a valid header ' +
  266             'with strict parsing enabled.'));
  267         }
  268       } else if (h === '(request-target)') {
  269         parsed.signingString +=
  270           '(request-target): ' + request.method.toLowerCase() + ' ' +
  271           request.url;
  272       } else {
  273         var value = request.headers[h];
  274         if (value === undefined)
  275           throw new MissingHeaderError(h + ' was not in the request');
  276         parsed.signingString += h + ': ' + value;
  277       }
  278 
  279       if ((i + 1) < parsed.params.headers.length)
  280         parsed.signingString += '\n';
  281     }
  282 
  283     // Check against the constraints
  284     var date;
  285     if (request.headers.date || request.headers['x-date']) {
  286         if (request.headers['x-date']) {
  287           date = new Date(request.headers['x-date']);
  288         } else {
  289           date = new Date(request.headers.date);
  290         }
  291       var now = new Date();
  292       var skew = Math.abs(now.getTime() - date.getTime());
  293 
  294       if (skew > options.clockSkew * 1000) {
  295         throw new ExpiredRequestError('clock skew of ' +
  296                                       (skew / 1000) +
  297                                       's was greater than ' +
  298                                       options.clockSkew + 's');
  299       }
  300     }
  301 
  302     options.headers.forEach(function (hdr) {
  303       // Remember that we already checked any headers in the params
  304       // were in the request, so if this passes we're good.
  305       if (parsed.params.headers.indexOf(hdr) < 0)
  306         throw new MissingHeaderError(hdr + ' was not a signed header');
  307     });
  308 
  309     if (options.algorithms) {
  310       if (options.algorithms.indexOf(parsed.params.algorithm) === -1)
  311         throw new InvalidParamsError(parsed.params.algorithm +
  312                                      ' is not a supported algorithm');
  313     }
  314 
  315     return parsed;
  316   }
  317 
  318 };