mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-30 18:24:03 +00:00
Merge pull request #1639 from christopherpickering/ntml-auth
Add NTML Auth Option for HTTP
This commit is contained in:
commit
0e8f6d2f85
8 changed files with 151 additions and 11 deletions
18
db/patch-add-other-auth.sql
Normal file
18
db/patch-add-other-auth.sql
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD auth_method VARCHAR(250);
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD auth_domain TEXT;
|
||||||
|
ALTER TABLE monitor
|
||||||
|
|
||||||
|
ADD auth_workstation TEXT;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
|
|
||||||
|
BEGIN TRANSACTION;
|
||||||
|
UPDATE monitor
|
||||||
|
SET auth_method = 'basic'
|
||||||
|
WHERE basic_auth_user is not null;
|
||||||
|
COMMIT;
|
47
package-lock.json
generated
47
package-lock.json
generated
|
@ -18,6 +18,7 @@
|
||||||
"args-parser": "~1.3.0",
|
"args-parser": "~1.3.0",
|
||||||
"axios": "~0.26.1",
|
"axios": "~0.26.1",
|
||||||
"axios-cached-dns-resolve": "^3.0.6",
|
"axios-cached-dns-resolve": "^3.0.6",
|
||||||
|
"axios-ntlm": "^1.3.0",
|
||||||
"badge-maker": "^3.3.1",
|
"badge-maker": "^3.3.1",
|
||||||
"bcryptjs": "~2.4.3",
|
"bcryptjs": "~2.4.3",
|
||||||
"bootstrap": "5.1.3",
|
"bootstrap": "5.1.3",
|
||||||
|
@ -4647,6 +4648,23 @@
|
||||||
"node": ">=14.0.0"
|
"node": ">=14.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/axios-ntlm": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios-ntlm/-/axios-ntlm-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-NPNsIMO1SGX5scs3ZWJqsV7iRLvET+DlRl94aZ7Sx14zA8RTQh9EDxsJmxB9cKjardKfp2Vge444uYYLfvWC0Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^0.21.3",
|
||||||
|
"dev-null": "^0.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/axios-ntlm/node_modules/axios": {
|
||||||
|
"version": "0.21.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
||||||
|
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||||
|
"dependencies": {
|
||||||
|
"follow-redirects": "^1.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/babel-jest": {
|
"node_modules/babel-jest": {
|
||||||
"version": "27.5.1",
|
"version": "27.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz",
|
||||||
|
@ -6635,6 +6653,11 @@
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/dev-null": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dev-null/-/dev-null-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-nMNZG0zfMgmdv8S5O0TM5cpwNbGKRGPCxVsr0SmA3NZZy9CYBbuNLL0PD3Acx9e5LIUgwONXtM9kM6RlawPxEQ=="
|
||||||
|
},
|
||||||
"node_modules/devtools-protocol": {
|
"node_modules/devtools-protocol": {
|
||||||
"version": "0.0.948846",
|
"version": "0.0.948846",
|
||||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.948846.tgz",
|
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.948846.tgz",
|
||||||
|
@ -22317,6 +22340,25 @@
|
||||||
"pino-pretty": "^7.6.1"
|
"pino-pretty": "^7.6.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"axios-ntlm": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios-ntlm/-/axios-ntlm-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-NPNsIMO1SGX5scs3ZWJqsV7iRLvET+DlRl94aZ7Sx14zA8RTQh9EDxsJmxB9cKjardKfp2Vge444uYYLfvWC0Q==",
|
||||||
|
"requires": {
|
||||||
|
"axios": "^0.21.3",
|
||||||
|
"dev-null": "^0.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": {
|
||||||
|
"version": "0.21.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz",
|
||||||
|
"integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==",
|
||||||
|
"requires": {
|
||||||
|
"follow-redirects": "^1.14.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"babel-jest": {
|
"babel-jest": {
|
||||||
"version": "27.5.1",
|
"version": "27.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-27.5.1.tgz",
|
||||||
|
@ -23843,6 +23885,11 @@
|
||||||
"integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
|
"integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"dev-null": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dev-null/-/dev-null-0.1.1.tgz",
|
||||||
|
"integrity": "sha512-nMNZG0zfMgmdv8S5O0TM5cpwNbGKRGPCxVsr0SmA3NZZy9CYBbuNLL0PD3Acx9e5LIUgwONXtM9kM6RlawPxEQ=="
|
||||||
|
},
|
||||||
"devtools-protocol": {
|
"devtools-protocol": {
|
||||||
"version": "0.0.948846",
|
"version": "0.0.948846",
|
||||||
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.948846.tgz",
|
"resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.948846.tgz",
|
||||||
|
|
|
@ -70,6 +70,7 @@
|
||||||
"args-parser": "~1.3.0",
|
"args-parser": "~1.3.0",
|
||||||
"axios": "~0.26.1",
|
"axios": "~0.26.1",
|
||||||
"axios-cached-dns-resolve": "^3.0.6",
|
"axios-cached-dns-resolve": "^3.0.6",
|
||||||
|
"axios-ntlm": "^1.3.0",
|
||||||
"badge-maker": "^3.3.1",
|
"badge-maker": "^3.3.1",
|
||||||
"bcryptjs": "~2.4.3",
|
"bcryptjs": "~2.4.3",
|
||||||
"bootstrap": "5.1.3",
|
"bootstrap": "5.1.3",
|
||||||
|
|
|
@ -59,6 +59,7 @@ class Database {
|
||||||
"patch-status-page-footer-css.sql": true,
|
"patch-status-page-footer-css.sql": true,
|
||||||
"patch-added-mqtt-monitor.sql": true,
|
"patch-added-mqtt-monitor.sql": true,
|
||||||
"patch-add-sqlserver-monitor.sql": true,
|
"patch-add-sqlserver-monitor.sql": true,
|
||||||
|
"patch-add-other-auth.sql": { parents: [ "patch-monitor-basic-auth.sql" ] },
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -7,7 +7,7 @@ dayjs.extend(timezone);
|
||||||
const axios = require("axios");
|
const axios = require("axios");
|
||||||
const { Prometheus } = require("../prometheus");
|
const { Prometheus } = require("../prometheus");
|
||||||
const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
|
const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
|
||||||
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, mqttAsync, setSetting } = require("../util-server");
|
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, mqttAsync, setSetting, httpNtlm } = require("../util-server");
|
||||||
const { R } = require("redbean-node");
|
const { R } = require("redbean-node");
|
||||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
const { BeanModel } = require("redbean-node/dist/bean-model");
|
||||||
const { Notification } = require("../notification");
|
const { Notification } = require("../notification");
|
||||||
|
@ -96,6 +96,9 @@ class Monitor extends BeanModel {
|
||||||
mqttSuccessMessage: this.mqttSuccessMessage,
|
mqttSuccessMessage: this.mqttSuccessMessage,
|
||||||
databaseConnectionString: this.databaseConnectionString,
|
databaseConnectionString: this.databaseConnectionString,
|
||||||
databaseQuery: this.databaseQuery,
|
databaseQuery: this.databaseQuery,
|
||||||
|
authMethod: this.authMethod,
|
||||||
|
authWorkstation: this.authWorkstation,
|
||||||
|
authDomain: this.authDomain,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (includeSensitiveData) {
|
if (includeSensitiveData) {
|
||||||
|
@ -221,7 +224,7 @@ class Monitor extends BeanModel {
|
||||||
|
|
||||||
// HTTP basic auth
|
// HTTP basic auth
|
||||||
let basicAuthHeader = {};
|
let basicAuthHeader = {};
|
||||||
if (this.basic_auth_user) {
|
if (this.auth_method === "basic") {
|
||||||
basicAuthHeader = {
|
basicAuthHeader = {
|
||||||
"Authorization": "Basic " + this.encodeBase64(this.basic_auth_user, this.basic_auth_pass),
|
"Authorization": "Basic " + this.encodeBase64(this.basic_auth_user, this.basic_auth_pass),
|
||||||
};
|
};
|
||||||
|
@ -272,7 +275,21 @@ class Monitor extends BeanModel {
|
||||||
log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`);
|
log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`);
|
||||||
log.debug("monitor", `[${this.name}] Axios Request`);
|
log.debug("monitor", `[${this.name}] Axios Request`);
|
||||||
|
|
||||||
let res = await axiosClient.request(options);
|
let res;
|
||||||
|
if (this.auth_method === "ntlm") {
|
||||||
|
options.httpsAgent.keepAlive = true;
|
||||||
|
|
||||||
|
res = await httpNtlm(options, {
|
||||||
|
username: this.basic_auth_user,
|
||||||
|
password: this.basic_auth_pass,
|
||||||
|
domain: this.authDomain,
|
||||||
|
workstation: this.authWorkstation,
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
res = await axiosClient.request(options);
|
||||||
|
}
|
||||||
|
|
||||||
bean.msg = `${res.status} - ${res.statusText}`;
|
bean.msg = `${res.status} - ${res.statusText}`;
|
||||||
bean.ping = dayjs().valueOf() - startTime;
|
bean.ping = dayjs().valueOf() - startTime;
|
||||||
|
|
||||||
|
|
|
@ -671,6 +671,9 @@ let needSetup = false;
|
||||||
bean.mqttSuccessMessage = monitor.mqttSuccessMessage;
|
bean.mqttSuccessMessage = monitor.mqttSuccessMessage;
|
||||||
bean.databaseConnectionString = monitor.databaseConnectionString;
|
bean.databaseConnectionString = monitor.databaseConnectionString;
|
||||||
bean.databaseQuery = monitor.databaseQuery;
|
bean.databaseQuery = monitor.databaseQuery;
|
||||||
|
bean.authMethod = monitor.authMethod;
|
||||||
|
bean.authWorkstation = monitor.authWorkstation;
|
||||||
|
bean.authDomain = monitor.authDomain;
|
||||||
|
|
||||||
await R.store(bean);
|
await R.store(bean);
|
||||||
|
|
||||||
|
@ -1244,8 +1247,11 @@ let needSetup = false;
|
||||||
method: monitorListData[i].method || "GET",
|
method: monitorListData[i].method || "GET",
|
||||||
body: monitorListData[i].body,
|
body: monitorListData[i].body,
|
||||||
headers: monitorListData[i].headers,
|
headers: monitorListData[i].headers,
|
||||||
|
authMethod: monitorListData[i].authMethod,
|
||||||
basic_auth_user: monitorListData[i].basic_auth_user,
|
basic_auth_user: monitorListData[i].basic_auth_user,
|
||||||
basic_auth_pass: monitorListData[i].basic_auth_pass,
|
basic_auth_pass: monitorListData[i].basic_auth_pass,
|
||||||
|
authWorkstation: monitorListData[i].authWorkstation,
|
||||||
|
authDomain: monitorListData[i].authDomain,
|
||||||
interval: monitorListData[i].interval,
|
interval: monitorListData[i].interval,
|
||||||
retryInterval: retryInterval,
|
retryInterval: retryInterval,
|
||||||
hostname: monitorListData[i].hostname,
|
hostname: monitorListData[i].hostname,
|
||||||
|
|
|
@ -11,6 +11,7 @@ const mqtt = require("mqtt");
|
||||||
const chroma = require("chroma-js");
|
const chroma = require("chroma-js");
|
||||||
const { badgeConstants } = require("./config");
|
const { badgeConstants } = require("./config");
|
||||||
const mssql = require("mssql");
|
const mssql = require("mssql");
|
||||||
|
const { NtlmClient } = require("axios-ntlm");
|
||||||
|
|
||||||
// From ping-lite
|
// From ping-lite
|
||||||
exports.WIN = /^win/.test(process.platform);
|
exports.WIN = /^win/.test(process.platform);
|
||||||
|
@ -173,6 +174,26 @@ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use NTLM Auth for a http request.
|
||||||
|
* @param {Object} options The http request options
|
||||||
|
* @param {Object} ntlmOptions The auth options
|
||||||
|
* @returns {Promise<(string[]|Object[]|Object)>}
|
||||||
|
*/
|
||||||
|
exports.httpNtlm = function (options, ntlmOptions) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
let client = NtlmClient(ntlmOptions);
|
||||||
|
|
||||||
|
client(options)
|
||||||
|
.then((resp) => {
|
||||||
|
resolve(resp);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
reject(err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves a given record using the specified DNS server
|
* Resolves a given record using the specified DNS server
|
||||||
* @param {string} hostname The hostname of the record to lookup
|
* @param {string} hostname The hostname of the record to lookup
|
||||||
|
|
|
@ -368,18 +368,46 @@
|
||||||
<textarea id="headers" v-model="monitor.headers" class="form-control" :placeholder="headersPlaceholder"></textarea>
|
<textarea id="headers" v-model="monitor.headers" class="form-control" :placeholder="headersPlaceholder"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- HTTP Basic Auth -->
|
<!-- HTTP Auth -->
|
||||||
<h4 class="mt-5 mb-2">{{ $t("HTTP Basic Auth") }}</h4>
|
<h4 class="mt-5 mb-2">{{ $t("HTTP Authentication") }}</h4>
|
||||||
|
|
||||||
|
<!-- Method -->
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<label for="basicauth" class="form-label">{{ $t("Username") }}</label>
|
<label for="method" class="form-label">{{ $t("Method") }}</label>
|
||||||
<input id="basicauth-user" v-model="monitor.basic_auth_user" type="text" class="form-control" :placeholder="$t('Username')">
|
<select id="method" v-model="monitor.authMethod" class="form-select">
|
||||||
|
<option :value="null">
|
||||||
|
None
|
||||||
|
</option>
|
||||||
|
<option value="basic">
|
||||||
|
Basic
|
||||||
|
</option>
|
||||||
|
<option value="ntlm">
|
||||||
|
NTLM
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<template v-if="monitor.authMethod && monitor.authMethod !== null ">
|
||||||
|
<div class="my-3">
|
||||||
|
<label for="basicauth" class="form-label">{{ $t("Username") }}</label>
|
||||||
|
<input id="basicauth-user" v-model="monitor.basic_auth_user" type="text" class="form-control" :placeholder="$t('Username')">
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<label for="basicauth" class="form-label">{{ $t("Password") }}</label>
|
<label for="basicauth" class="form-label">{{ $t("Password") }}</label>
|
||||||
<input id="basicauth-pass" v-model="monitor.basic_auth_pass" type="password" autocomplete="new-password" class="form-control" :placeholder="$t('Password')">
|
<input id="basicauth-pass" v-model="monitor.basic_auth_pass" type="password" autocomplete="new-password" class="form-control" :placeholder="$t('Password')">
|
||||||
</div>
|
</div>
|
||||||
|
<template v-if="monitor.authMethod === 'ntlm' ">
|
||||||
|
<div class="my-3">
|
||||||
|
<label for="basicauth" class="form-label">{{ $t("Domain") }}</label>
|
||||||
|
<input id="basicauth-domain" v-model="monitor.authDomain" type="text" class="form-control" :placeholder="$t('Domain')">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="my-3">
|
||||||
|
<label for="basicauth" class="form-label">{{ $t("Workstation") }}</label>
|
||||||
|
<input id="basicauth-workstation" v-model="monitor.authWorkstation" type="password" autocomplete="new-password" class="form-control" :placeholder="$t('Workstation')">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -569,6 +597,7 @@ export default {
|
||||||
mqttPassword: "",
|
mqttPassword: "",
|
||||||
mqttTopic: "",
|
mqttTopic: "",
|
||||||
mqttSuccessMessage: "",
|
mqttSuccessMessage: "",
|
||||||
|
authMethod: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (this.$root.proxyList && !this.monitor.proxyId) {
|
if (this.$root.proxyList && !this.monitor.proxyId) {
|
||||||
|
|
Loading…
Reference in a new issue