UNPKG

marklogic

Version:

The official MarkLogic Node.js client API.

203 lines (189 loc) 6.58 kB
/* * Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved. */ /* * www-authenticate * https://github.com/randymized/www-authenticate * * Copyright (c) 2013 Randy McLaughlin * Licensed under the MIT license. */ 'use strict'; const crypto= require('crypto') , parsers= require('./parsers') , md5= require('./md5') , user_credentials= require('./user-credentials') , basic_challenge= { statusCode: 401, headers: { 'www-authenticate': 'Basic realm="sample"' } } ; function hex8(num) { return ('00000000' + num.toString(16)).slice(-8); } const www_authenticator = function(username,password,options) { if (2 == arguments.length && toString.call(password) != '[object String]') { options= password; password= null; } const credentials= user_credentials(username,password); let cnonce; if (options) { if (toString.call(options.cnonce) == '[object String]') {cnonce= options.cnonce;} } if (cnonce === void 0) {cnonce= crypto.pseudoRandomBytes(8).toString('hex');} const parse_header= function(www_authenticate) { function Authenticator() { function note_error(err) { this.err= err; } let nc= 0; const parsed= new parsers.WWW_Authenticate(www_authenticate); if (parsed.err) {return note_error(parsed.err);} const auth_parms= this.parms= parsed.parms; this.cnonce= cnonce; switch(parsed.scheme) { case 'Basic': var auth_string= 'Basic '+credentials.basic(); this.authorize= function() { return auth_string; }; return; case 'Digest': var realm= auth_parms.realm; if (!realm) { return note_error('Realm not found in www-authenticate header.'); } var ha1= credentials.digest(realm); var nonce= auth_parms.nonce; if (!nonce) { return note_error('Nonce not found in www-authenticate header.'); } var fixed= 'Digest username="'+credentials.username+'",'+ ' realm="'+realm+'",'+ ' nonce="'+nonce+'",'; var qop= auth_parms.qop; if (!qop) { this.authorize= function(method,digestURI) { const ha2= md5(method+':'+digestURI); return fixed+ ' uri="'+digestURI+'",'+ ' response="'+md5(ha1+':'+nonce+':'+ha2)+'",'; }; return; } else { const qopa= qop.split(','); let _i, _len; for (_i = 0, _len = qopa.length; _i < _len; _i++) { if ('auth' === qopa[_i]) { var opaque= auth_parms.opaque; let algorithm= auth_parms.algorithm; if (algorithm) { fixed+= ' algorithm="'+algorithm+'",'; } else { algorithm= 'MD5'; } var a1= 'MD5-sess' == algorithm ? md5(ha1+':'+nonce+':'+cnonce) : ha1; this.authorize= function(method,digestURI) { const ha2= md5(method+':'+digestURI); nc= nc+1; const hexed_nc= hex8(nc); let s= fixed+ ' uri="'+digestURI+'",'+ ' qop=auth,'+ ' nc='+hexed_nc+','+ ' cnonce="'+cnonce+'",'+ ' response="'+md5(a1+':'+nonce+':'+hexed_nc+':'+cnonce+':auth:'+ha2)+'"'; if (opaque) { s+= ', opaque="'+opaque+'"'; } return s; }; return; } } return note_error('Server does not accept any supported quality of protection techniques.'); } default: return note_error('Unknown scheme'); } } return new Authenticator(); }; parse_header.authenticator= new HigherLevel(credentials,options); // deprecated return parse_header; }; function HigherLevel(credentials,options) { this.credentials= credentials; this.options= options; if (options && options.sendImmediately) { this.sendImmediately= true; } } HigherLevel.prototype.get_challenge= function(request) { if (401 == request.statusCode && 'www-authenticate' in request.headers) { if (!this.parse_header) { this.parse_header= www_authenticator(this.credentials,this.options); } this.challenge= this.parse_header(request.headers['www-authenticate']); return this.challenge.err; } }; HigherLevel.prototype._challenge= function() { if (!this.challenge) { if (this.sendImmediately) { // simulate receipt of a basic challenge this.get_challenge(basic_challenge); return this.challenge; } else {return;} // simply won't produce an 'Authorization' header } return this.challenge; }; HigherLevel.prototype.authentication_string= function(method,digestURI) { const challenge= this._challenge(); if (!challenge) {return;} // simply won't produce an 'Authorization' header if (challenge.err) {return challenge.err;} return challenge.authorize(method,digestURI); }; HigherLevel.prototype.authenticate_headers= function(headers,method,digestURI) { const challenge= this._challenge(); if (!challenge) {return;} // simply won't produce an 'Authorization' header if (challenge.err) {return challenge.err;} headers.authorization= challenge.authorize(method,digestURI); }; HigherLevel.prototype.authenticate_request_options= function(request_options) { const challenge= this._challenge(); if (!challenge) {return;} // simply won't produce an 'Authorization' header if (challenge.err) {return challenge.err;} if (!request_options.headers) {request_options.headers= {};} request_options.headers.authorization= challenge.authorize(request_options.method,request_options.path); }; module.exports = www_authenticator; module.exports.parsers= parsers; module.exports.user_credentials= user_credentials; module.exports.basic_challenge= basic_challenge; module.exports.authenticator= function(username,password,options) { if (2 == arguments.length && toString.call(password) != '[object String]') { options= password; password= null; } const credentials= user_credentials(username,password); return new HigherLevel(credentials,options); };