diff --git a/plugins/dbgate-plugin-mssql/package.json b/plugins/dbgate-plugin-mssql/package.json index 7bb280ef7..587be19ad 100644 --- a/plugins/dbgate-plugin-mssql/package.json +++ b/plugins/dbgate-plugin-mssql/package.json @@ -36,6 +36,7 @@ "webpack-cli": "^5.1.4" }, "dependencies": { + "@azure/identity": "^4.6.0", "async-lock": "^1.2.6", "dbgate-query-splitter": "^4.11.3", "dbgate-tools": "^6.0.0-alpha.1", diff --git a/plugins/dbgate-plugin-mssql/src/backend/driver.js b/plugins/dbgate-plugin-mssql/src/backend/driver.js index 5cd719b92..b3c59941e 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/driver.js +++ b/plugins/dbgate-plugin-mssql/src/backend/driver.js @@ -72,6 +72,14 @@ const driver = { } ); } + if (!platformInfo.isElectron) { + res.push({ + title: 'Azure Managed Identity', + name: 'azureManagedIdentity', + disabledFields: ['user', 'password'], + }); + } + if (res.length > 0) { return _.uniqBy(res, 'name'); } @@ -80,7 +88,8 @@ const driver = { async connect(conn) { const { authType } = conn; - const connectionType = platformInfo?.isWindows && (authType == 'sspi' || authType == 'sql') ? 'msnodesqlv8' : 'tedious'; + const connectionType = + platformInfo?.isWindows && (authType == 'sspi' || authType == 'sql') ? 'msnodesqlv8' : 'tedious'; const client = connectionType == 'msnodesqlv8' ? await nativeConnect(conn) : await tediousConnect(conn); return { diff --git a/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js b/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js index f93234bf1..5f531c7ab 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js +++ b/plugins/dbgate-plugin-mssql/src/backend/tediousDriver.js @@ -1,6 +1,7 @@ const _ = require('lodash'); const stream = require('stream'); const tedious = require('tedious'); +const { ManagedIdentityCredential } = require('@azure/identity'); const makeUniqueColumnNames = require('./makeUniqueColumnNames'); const { extractDbNameFromComposite } = global.DBGATE_PACKAGES['dbgate-tools']; @@ -23,12 +24,50 @@ function extractTediousColumns(columns, addDriverNativeColumn = false) { return res; } +async function getDefaultAzureSqlToken() { + const credential = new ManagedIdentityCredential(); + const tokenResponse = await credential.getToken('https://database.windows.net/.default'); + return tokenResponse.token; +} + +async function getAuthentication({ authType, accessToken, user, password, windowsDomain }) { + switch (authType) { + case 'azureManagedIdentity': + const token = await getDefaultAzureSqlToken(); + return { + type: 'azure-active-directory-access-token', + options: { + token, + }, + }; + + case 'msentra': + return { + type: 'azure-active-directory-access-token', + options: { + token: accessToken, + }, + }; + default: + return { + type: windowsDomain ? 'ntlm' : 'default', + options: { + userName: user, + password: password, + ...(windowsDomain ? { domain: windowsDomain } : {}), + }, + }; + } +} + async function tediousConnect(storedConnection) { - const { server, port, user, password, database, ssl, trustServerCertificate, windowsDomain, authType, accessToken } = - storedConnection; + const { server, port, database, ssl, trustServerCertificate, authType } = storedConnection; + + const authentication = await getAuthentication(storedConnection); + return new Promise((resolve, reject) => { const connectionOptions = { - encrypt: !!ssl || authType == 'msentra', + encrypt: !!ssl || authType == 'msentra' || authType == 'azureManagedIdentity', cryptoCredentialsDetails: ssl ? _.pick(ssl, ['ca', 'cert', 'key']) : undefined, trustServerCertificate: ssl ? (!ssl.ca && !ssl.cert && !ssl.key ? true : ssl.rejectUnauthorized) : undefined, enableArithAbort: true, @@ -43,23 +82,6 @@ async function tediousConnect(storedConnection) { connectionOptions.database = extractDbNameFromComposite(database); } - const authentication = - authType == 'msentra' - ? { - type: 'azure-active-directory-access-token', - options: { - token: accessToken, - }, - } - : { - type: windowsDomain ? 'ntlm' : 'default', - options: { - userName: user, - password: password, - ...(windowsDomain ? { domain: windowsDomain } : {}), - }, - }; - const connection = new tedious.Connection({ server, authentication, diff --git a/yarn.lock b/yarn.lock index 200b3c388..1ec9fdafb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -542,7 +542,16 @@ "@azure/core-util" "^1.1.0" tslib "^2.6.2" -"@azure/core-client@^1.3.0", "@azure/core-client@^1.4.0", "@azure/core-client@^1.5.0": +"@azure/core-auth@^1.8.0", "@azure/core-auth@^1.9.0": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@azure/core-auth/-/core-auth-1.9.0.tgz#ac725b03fabe3c892371065ee9e2041bee0fd1ac" + integrity sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-util" "^1.11.0" + tslib "^2.6.2" + +"@azure/core-client@^1.3.0", "@azure/core-client@^1.4.0", "@azure/core-client@^1.5.0", "@azure/core-client@^1.9.2": version "1.9.2" resolved "https://registry.yarnpkg.com/@azure/core-client/-/core-client-1.9.2.tgz#6fc69cee2816883ab6c5cdd653ee4f2ff9774f74" integrity sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w== @@ -595,6 +604,20 @@ https-proxy-agent "^7.0.0" tslib "^2.6.2" +"@azure/core-rest-pipeline@^1.17.0": + version "1.18.2" + resolved "https://registry.yarnpkg.com/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.2.tgz#fa3a83b412d4b3e33edca30a71b1d5838306c075" + integrity sha512-IkTf/DWKyCklEtN/WYW3lqEsIaUDshlzWRlZNNwSYtFcCBQz++OtOjxNpm8rr1VcbMS6RpjybQa3u6B6nG0zNw== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-auth" "^1.8.0" + "@azure/core-tracing" "^1.0.1" + "@azure/core-util" "^1.11.0" + "@azure/logger" "^1.0.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.0" + tslib "^2.6.2" + "@azure/core-tracing@^1.0.0", "@azure/core-tracing@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@azure/core-tracing/-/core-tracing-1.1.2.tgz#065dab4e093fb61899988a1cdbc827d9ad90b4ee" @@ -610,6 +633,14 @@ "@azure/abort-controller" "^2.0.0" tslib "^2.6.2" +"@azure/core-util@^1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@azure/core-util/-/core-util-1.11.0.tgz#f530fc67e738aea872fbdd1cc8416e70219fada7" + integrity sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g== + dependencies: + "@azure/abort-controller" "^2.0.0" + tslib "^2.6.2" + "@azure/identity@^3.4.1": version "3.4.2" resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-3.4.2.tgz#6b01724c9caac7cadab6b63c76584345bda8e2de" @@ -630,6 +661,26 @@ stoppable "^1.1.0" tslib "^2.2.0" +"@azure/identity@^4.6.0": + version "4.6.0" + resolved "https://registry.yarnpkg.com/@azure/identity/-/identity-4.6.0.tgz#276957b59fed96cf48d5e50fc728c3c226e4f105" + integrity sha512-ANpO1iAvcZmpD4QY7/kaE/P2n66pRXsDp3nMUC6Ow3c9KfXOZF7qMU9VgqPw8m7adP7TVIbVyrCEmD9cth3KQQ== + dependencies: + "@azure/abort-controller" "^2.0.0" + "@azure/core-auth" "^1.9.0" + "@azure/core-client" "^1.9.2" + "@azure/core-rest-pipeline" "^1.17.0" + "@azure/core-tracing" "^1.0.0" + "@azure/core-util" "^1.11.0" + "@azure/logger" "^1.0.0" + "@azure/msal-browser" "^4.0.1" + "@azure/msal-node" "^2.15.0" + events "^3.0.0" + jws "^4.0.0" + open "^8.0.0" + stoppable "^1.1.0" + tslib "^2.2.0" + "@azure/keyvault-keys@^4.4.0": version "4.8.0" resolved "https://registry.yarnpkg.com/@azure/keyvault-keys/-/keyvault-keys-4.8.0.tgz#1513b3a187bb3a9a372b5980c593962fb793b2ad" @@ -661,11 +712,37 @@ dependencies: "@azure/msal-common" "14.10.0" +"@azure/msal-browser@^4.0.1": + version "4.2.0" + resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-4.2.0.tgz#3d817357cfb0e6aef68bb708df7ccce9fe14ca65" + integrity sha512-MXQjgAgjg/2VRKV+UPWHESoZPcue2ZvWKfpBLCyTUyixP+mhCl0q5D1+xDiwBGV3lru2poKZVZDQAOE40wKmWg== + dependencies: + "@azure/msal-common" "15.1.1" + "@azure/msal-common@14.10.0": version "14.10.0" resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.10.0.tgz#215449726717b53d549953db77562cad6cb8421c" integrity sha512-Zk6DPDz7e1wPgLoLgAp0349Yay9RvcjPM5We/ehuenDNsz/t9QEFI7tRoHpp/e47I4p20XE3FiDlhKwAo3utDA== +"@azure/msal-common@14.16.0": + version "14.16.0" + resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.16.0.tgz#f3470fcaec788dbe50859952cd499340bda23d7a" + integrity sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA== + +"@azure/msal-common@15.1.1": + version "15.1.1" + resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-15.1.1.tgz#2bd9bef17857217f25b5885e7b2824bfd9c8edab" + integrity sha512-bvLWYq9fleAcTJ6H+hfkG91On6vI/UhGyOB7Z6r0Bsa+KTL3zPtigmGCOJgdxrEklOYD88X9SehexLDH/5NRKQ== + +"@azure/msal-node@^2.15.0": + version "2.16.2" + resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-2.16.2.tgz#3eb768d36883ea6f9a939c0b5b467b518e78fffc" + integrity sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ== + dependencies: + "@azure/msal-common" "14.16.0" + jsonwebtoken "^9.0.0" + uuid "^8.3.0" + "@azure/msal-node@^2.5.1": version "2.8.0" resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-2.8.0.tgz#ef6e4a76bcd0851f7b1240d94b00fe1f9a52d559"