wzp
2021-05-13 7d694a9113118daec5be7ac224dab46a3b20f106
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright 2012 Mark Cavage, Inc.  All rights reserved.
 
'use strict';
 
var httpSignature = require('http-signature');
var errors = require('restify-errors');
 
///--- Globals
 
var InvalidHeaderError = errors.InvalidHeaderError;
 
var OPTIONS = {
    algorithms: [
        'rsa-sha1',
        'rsa-sha256',
        'rsa-sha512',
        'dsa-sha1',
        'hmac-sha1',
        'hmac-sha256',
        'hmac-sha512'
    ]
};
 
///--- Helpers
 
function parseBasic(string) {
    var decoded;
    var index;
    var pieces;
 
    decoded = new Buffer(string, 'base64').toString('utf8');
 
    if (!decoded) {
        throw new InvalidHeaderError('Authorization header invalid');
    }
 
    index = decoded.indexOf(':');
 
    if (index === -1) {
        pieces = [decoded];
    } else {
        pieces = [decoded.slice(0, index), decoded.slice(index + 1)];
    }
 
    if (!pieces || typeof pieces[0] !== 'string') {
        throw new InvalidHeaderError('Authorization header invalid');
    }
 
    // Allows for usernameless authentication
    if (!pieces[0]) {
        pieces[0] = null;
    }
 
    // Allows for passwordless authentication
    if (!pieces[1]) {
        pieces[1] = null;
    }
 
    return {
        username: pieces[0],
        password: pieces[1]
    };
}
 
function parseSignature(request, options) {
    var opts = options || {};
    opts.algorithms = OPTIONS.algorithms;
 
    try {
        return httpSignature.parseRequest(request, options);
    } catch (e) {
        throw new InvalidHeaderError(
            'Authorization header invalid: ' + e.message
        );
    }
}
 
/**
 * Parses out the `Authorization` header as best restify can.
 * Currently only HTTP Basic Auth and
 * [HTTP Signature](https://github.com/joyent/node-http-signature)
 * schemes are supported.
 *
 * @public
 * @function authorizationParser
 * @throws   {InvalidArgumentError}
 * @param    {Object} [options] - an optional options object that is
 *                                passed to http-signature
 * @returns  {Function} Handler
 * @example
 * <caption>
 * Subsequent handlers will see `req.authorization`, which looks like above.
 *
 * `req.username` will also be set, and defaults to 'anonymous'.  If the scheme
 * is unrecognized, the only thing available in `req.authorization` will be
 * `scheme` and `credentials` - it will be up to you to parse out the rest.
 * </caption>
 * {
 *   scheme: "<Basic|Signature|...>",
 *   credentials: "<Undecoded value of header>",
 *   basic: {
 *     username: $user
 *     password: $password
 *   }
 * }
 */
function authorizationParser(options) {
    function parseAuthorization(req, res, next) {
        req.authorization = {};
        req.username = 'anonymous';
 
        if (!req.headers.authorization) {
            return next();
        }
 
        var pieces = req.headers.authorization.split(' ', 2);
 
        if (!pieces || pieces.length !== 2) {
            var e = new InvalidHeaderError('BasicAuth content is invalid.');
            return next(e);
        }
 
        req.authorization.scheme = pieces[0];
        req.authorization.credentials = pieces[1];
 
        try {
            switch (pieces[0].toLowerCase()) {
                case 'basic':
                    req.authorization.basic = parseBasic(pieces[1]);
                    req.username = req.authorization.basic.username;
                    break;
 
                case 'signature':
                    req.authorization.signature = parseSignature(req, options);
                    req.username = req.authorization.signature.keyId;
                    break;
 
                default:
                    break;
            }
        } catch (e2) {
            return next(e2);
        }
 
        return next();
    }
 
    return parseAuthorization;
}
 
module.exports = authorizationParser;