mirror of
https://github.com/gradle/gradle-build-action.git
synced 2024-10-19 20:41:12 +08:00
138 lines
6.3 KiB
JavaScript
138 lines
6.3 KiB
JavaScript
|
"use strict";
|
||
|
// Copyright (c) Microsoft. All rights reserved.
|
||
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||
|
Object.defineProperty(exports, "__esModule", { value: true });
|
||
|
const http = require("http");
|
||
|
const https = require("https");
|
||
|
const _ = require("underscore");
|
||
|
const ntlm = require("../opensource/node-http-ntlm/ntlm");
|
||
|
class NtlmCredentialHandler {
|
||
|
constructor(username, password, workstation, domain) {
|
||
|
this._ntlmOptions = {};
|
||
|
this._ntlmOptions.username = username;
|
||
|
this._ntlmOptions.password = password;
|
||
|
if (domain !== undefined) {
|
||
|
this._ntlmOptions.domain = domain;
|
||
|
}
|
||
|
else {
|
||
|
this._ntlmOptions.domain = '';
|
||
|
}
|
||
|
if (workstation !== undefined) {
|
||
|
this._ntlmOptions.workstation = workstation;
|
||
|
}
|
||
|
else {
|
||
|
this._ntlmOptions.workstation = '';
|
||
|
}
|
||
|
}
|
||
|
prepareRequest(options) {
|
||
|
// No headers or options need to be set. We keep the credentials on the handler itself.
|
||
|
// If a (proxy) agent is set, remove it as we don't support proxy for NTLM at this time
|
||
|
if (options.agent) {
|
||
|
delete options.agent;
|
||
|
}
|
||
|
}
|
||
|
canHandleAuthentication(response) {
|
||
|
if (response && response.message && response.message.statusCode === 401) {
|
||
|
// Ensure that we're talking NTLM here
|
||
|
// Once we have the www-authenticate header, split it so we can ensure we can talk NTLM
|
||
|
const wwwAuthenticate = response.message.headers['www-authenticate'];
|
||
|
if (wwwAuthenticate) {
|
||
|
const mechanisms = wwwAuthenticate.split(', ');
|
||
|
const index = mechanisms.indexOf("NTLM");
|
||
|
if (index >= 0) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
handleAuthentication(httpClient, requestInfo, objs) {
|
||
|
return new Promise((resolve, reject) => {
|
||
|
const callbackForResult = function (err, res) {
|
||
|
if (err) {
|
||
|
reject(err);
|
||
|
}
|
||
|
// We have to readbody on the response before continuing otherwise there is a hang.
|
||
|
res.readBody().then(() => {
|
||
|
resolve(res);
|
||
|
});
|
||
|
};
|
||
|
this.handleAuthenticationPrivate(httpClient, requestInfo, objs, callbackForResult);
|
||
|
});
|
||
|
}
|
||
|
handleAuthenticationPrivate(httpClient, requestInfo, objs, finalCallback) {
|
||
|
// Set up the headers for NTLM authentication
|
||
|
requestInfo.options = _.extend(requestInfo.options, {
|
||
|
username: this._ntlmOptions.username,
|
||
|
password: this._ntlmOptions.password,
|
||
|
domain: this._ntlmOptions.domain,
|
||
|
workstation: this._ntlmOptions.workstation
|
||
|
});
|
||
|
if (httpClient.isSsl === true) {
|
||
|
requestInfo.options.agent = new https.Agent({ keepAlive: true });
|
||
|
}
|
||
|
else {
|
||
|
requestInfo.options.agent = new http.Agent({ keepAlive: true });
|
||
|
}
|
||
|
let self = this;
|
||
|
// The following pattern of sending the type1 message following immediately (in a setImmediate) is
|
||
|
// critical for the NTLM exchange to happen. If we removed setImmediate (or call in a different manner)
|
||
|
// the NTLM exchange will always fail with a 401.
|
||
|
this.sendType1Message(httpClient, requestInfo, objs, function (err, res) {
|
||
|
if (err) {
|
||
|
return finalCallback(err, null, null);
|
||
|
}
|
||
|
/// We have to readbody on the response before continuing otherwise there is a hang.
|
||
|
res.readBody().then(() => {
|
||
|
// It is critical that we have setImmediate here due to how connection requests are queued.
|
||
|
// If setImmediate is removed then the NTLM handshake will not work.
|
||
|
// setImmediate allows us to queue a second request on the same connection. If this second
|
||
|
// request is not queued on the connection when the first request finishes then node closes
|
||
|
// the connection. NTLM requires both requests to be on the same connection so we need this.
|
||
|
setImmediate(function () {
|
||
|
self.sendType3Message(httpClient, requestInfo, objs, res, finalCallback);
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
// The following method is an adaptation of code found at https://github.com/SamDecrock/node-http-ntlm/blob/master/httpntlm.js
|
||
|
sendType1Message(httpClient, requestInfo, objs, finalCallback) {
|
||
|
const type1msg = ntlm.createType1Message(this._ntlmOptions);
|
||
|
const type1options = {
|
||
|
headers: {
|
||
|
'Connection': 'keep-alive',
|
||
|
'Authorization': type1msg
|
||
|
},
|
||
|
timeout: requestInfo.options.timeout || 0,
|
||
|
agent: requestInfo.httpModule,
|
||
|
};
|
||
|
const type1info = {};
|
||
|
type1info.httpModule = requestInfo.httpModule;
|
||
|
type1info.parsedUrl = requestInfo.parsedUrl;
|
||
|
type1info.options = _.extend(type1options, _.omit(requestInfo.options, 'headers'));
|
||
|
return httpClient.requestRawWithCallback(type1info, objs, finalCallback);
|
||
|
}
|
||
|
// The following method is an adaptation of code found at https://github.com/SamDecrock/node-http-ntlm/blob/master/httpntlm.js
|
||
|
sendType3Message(httpClient, requestInfo, objs, res, callback) {
|
||
|
if (!res.message.headers && !res.message.headers['www-authenticate']) {
|
||
|
throw new Error('www-authenticate not found on response of second request');
|
||
|
}
|
||
|
const type2msg = ntlm.parseType2Message(res.message.headers['www-authenticate']);
|
||
|
const type3msg = ntlm.createType3Message(type2msg, this._ntlmOptions);
|
||
|
const type3options = {
|
||
|
headers: {
|
||
|
'Authorization': type3msg,
|
||
|
'Connection': 'Close'
|
||
|
},
|
||
|
agent: requestInfo.httpModule,
|
||
|
};
|
||
|
const type3info = {};
|
||
|
type3info.httpModule = requestInfo.httpModule;
|
||
|
type3info.parsedUrl = requestInfo.parsedUrl;
|
||
|
type3options.headers = _.extend(type3options.headers, requestInfo.options.headers);
|
||
|
type3info.options = _.extend(type3options, _.omit(requestInfo.options, 'headers'));
|
||
|
return httpClient.requestRawWithCallback(type3info, objs, callback);
|
||
|
}
|
||
|
}
|
||
|
exports.NtlmCredentialHandler = NtlmCredentialHandler;
|