Initial commit
[yaffs-website] / node_modules / hawk / lib / utils.js
1 // Load modules\r
2 \r
3 var Sntp = require('sntp');\r
4 var Boom = require('boom');\r
5 \r
6 \r
7 // Declare internals\r
8 \r
9 var internals = {};\r
10 \r
11 \r
12 exports.version = function () {\r
13 \r
14     return require('../package.json').version;\r
15 };\r
16 \r
17 \r
18 exports.limits = {\r
19     maxMatchLength: 4096            // Limit the length of uris and headers to avoid a DoS attack on string matching\r
20 };\r
21 \r
22 \r
23 // Extract host and port from request\r
24 \r
25 //                                            $1                            $2\r
26 internals.hostHeaderRegex = /^(?:(?:\r\n)?\s)*((?:[^:]+)|(?:\[[^\]]+\]))(?::(\d+))?(?:(?:\r\n)?\s)*$/;              // (IPv4, hostname)|(IPv6)\r
27 \r
28 \r
29 exports.parseHost = function (req, hostHeaderName) {\r
30 \r
31     hostHeaderName = (hostHeaderName ? hostHeaderName.toLowerCase() : 'host');\r
32     var hostHeader = req.headers[hostHeaderName];\r
33     if (!hostHeader) {\r
34         return null;\r
35     }\r
36 \r
37     if (hostHeader.length > exports.limits.maxMatchLength) {\r
38         return null;\r
39     }\r
40 \r
41     var hostParts = hostHeader.match(internals.hostHeaderRegex);\r
42     if (!hostParts) {\r
43         return null;\r
44     }\r
45 \r
46     return {\r
47         name: hostParts[1],\r
48         port: (hostParts[2] ? hostParts[2] : (req.connection && req.connection.encrypted ? 443 : 80))\r
49     };\r
50 };\r
51 \r
52 \r
53 // Parse Content-Type header content\r
54 \r
55 exports.parseContentType = function (header) {\r
56 \r
57     if (!header) {\r
58         return '';\r
59     }\r
60 \r
61     return header.split(';')[0].trim().toLowerCase();\r
62 };\r
63 \r
64 \r
65 // Convert node's  to request configuration object\r
66 \r
67 exports.parseRequest = function (req, options) {\r
68 \r
69     if (!req.headers) {\r
70         return req;\r
71     }\r
72 \r
73     // Obtain host and port information\r
74 \r
75     var host;\r
76     if (!options.host ||\r
77         !options.port) {\r
78 \r
79         host = exports.parseHost(req, options.hostHeaderName);\r
80         if (!host) {\r
81             return new Error('Invalid Host header');\r
82         }\r
83     }\r
84 \r
85     var request = {\r
86         method: req.method,\r
87         url: req.url,\r
88         host: options.host || host.name,\r
89         port: options.port || host.port,\r
90         authorization: req.headers.authorization,\r
91         contentType: req.headers['content-type'] || ''\r
92     };\r
93 \r
94     return request;\r
95 };\r
96 \r
97 \r
98 exports.now = function (localtimeOffsetMsec) {\r
99 \r
100     return Sntp.now() + (localtimeOffsetMsec || 0);\r
101 };\r
102 \r
103 \r
104 exports.nowSecs = function (localtimeOffsetMsec) {\r
105 \r
106     return Math.floor(exports.now(localtimeOffsetMsec) / 1000);\r
107 };\r
108 \r
109 \r
110 internals.authHeaderRegex = /^(\w+)(?:\s+(.*))?$/;                                      // Header: scheme[ something]\r
111 internals.attributeRegex = /^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/;   // !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9\r
112 \r
113 \r
114 // Parse Hawk HTTP Authorization header\r
115 \r
116 exports.parseAuthorizationHeader = function (header, keys) {\r
117 \r
118     keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg'];\r
119 \r
120     if (!header) {\r
121         return Boom.unauthorized(null, 'Hawk');\r
122     }\r
123 \r
124     if (header.length > exports.limits.maxMatchLength) {\r
125         return Boom.badRequest('Header length too long');\r
126     }\r
127 \r
128     var headerParts = header.match(internals.authHeaderRegex);\r
129     if (!headerParts) {\r
130         return Boom.badRequest('Invalid header syntax');\r
131     }\r
132 \r
133     var scheme = headerParts[1];\r
134     if (scheme.toLowerCase() !== 'hawk') {\r
135         return Boom.unauthorized(null, 'Hawk');\r
136     }\r
137 \r
138     var attributesString = headerParts[2];\r
139     if (!attributesString) {\r
140         return Boom.badRequest('Invalid header syntax');\r
141     }\r
142 \r
143     var attributes = {};\r
144     var errorMessage = '';\r
145     var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {\r
146 \r
147         // Check valid attribute names\r
148 \r
149         if (keys.indexOf($1) === -1) {\r
150             errorMessage = 'Unknown attribute: ' + $1;\r
151             return;\r
152         }\r
153 \r
154         // Allowed attribute value characters\r
155 \r
156         if ($2.match(internals.attributeRegex) === null) {\r
157             errorMessage = 'Bad attribute value: ' + $1;\r
158             return;\r
159         }\r
160 \r
161         // Check for duplicates\r
162 \r
163         if (attributes.hasOwnProperty($1)) {\r
164             errorMessage = 'Duplicate attribute: ' + $1;\r
165             return;\r
166         }\r
167 \r
168         attributes[$1] = $2;\r
169         return '';\r
170     });\r
171 \r
172     if (verify !== '') {\r
173         return Boom.badRequest(errorMessage || 'Bad header format');\r
174     }\r
175 \r
176     return attributes;\r
177 };\r
178 \r
179 \r
180 exports.unauthorized = function (message, attributes) {\r
181 \r
182     return Boom.unauthorized(message, 'Hawk', attributes);\r
183 };\r
184 \r