"Fossies" - the Fresh Open Source Software Archive

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