diff --git a/packages/api/src/controllers/connections.js b/packages/api/src/controllers/connections.js index 697abecda..49a484883 100644 --- a/packages/api/src/controllers/connections.js +++ b/packages/api/src/controllers/connections.js @@ -16,6 +16,7 @@ const { safeJsonParse, getLogger } = require('dbgate-tools'); const platformInfo = require('../utility/platformInfo'); const { connectionHasPermission, testConnectionPermission } = require('../utility/hasPermission'); const pipeForkLogs = require('../utility/pipeForkLogs'); +const requireEngineDriver = require('../utility/requireEngineDriver'); const logger = getLogger('connections'); @@ -345,7 +346,7 @@ module.exports = { const storage = require('./storage'); - const storageConnection = await storage.getConnection({conid}); + const storageConnection = await storage.getConnection({ conid }); if (storageConnection) { return storageConnection; } @@ -379,4 +380,16 @@ module.exports = { }); return res; }, + + dblogin_meta: { + raw: true, + method: 'get', + }, + async dblogin(req, res) { + const { conid } = req.query; + const connection = await this.getCore({ conid }); + const driver = requireEngineDriver(connection); + const authUrl = driver.getRedirectAuthUrl(connection); + res.redirect(authUrl); + }, }; diff --git a/packages/api/src/controllers/databaseConnections.js b/packages/api/src/controllers/databaseConnections.js index 65f9fbd28..47321775f 100644 --- a/packages/api/src/controllers/databaseConnections.js +++ b/packages/api/src/controllers/databaseConnections.js @@ -89,6 +89,9 @@ module.exports = { if (connection.passwordMode == 'askPassword' || connection.passwordMode == 'askUser') { throw new MissingCredentialsError({ conid, passwordMode: connection.passwordMode }); } + if (connection.useRedirectDbLogin) { + throw new MissingCredentialsError({ conid, redirectToDbLogin: true }); + } const subprocess = fork( global['API_PACKAGE'] || process.argv[1], [ diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts index 2691436e9..176e2059b 100644 --- a/packages/types/engines.d.ts +++ b/packages/types/engines.d.ts @@ -149,6 +149,7 @@ export interface EngineDriver { summaryCommand(pool, command, row): Promise; startProfiler(pool, options): Promise; stopProfiler(pool, profiler): Promise; + getRedirectAuthUrl(connection): Promise; analyserClass?: any; dumperClass?: any; diff --git a/packages/web/src/settings/ConnectionDriverFields.svelte b/packages/web/src/settings/ConnectionDriverFields.svelte index 70e856023..984302293 100644 --- a/packages/web/src/settings/ConnectionDriverFields.svelte +++ b/packages/web/src/settings/ConnectionDriverFields.svelte @@ -31,7 +31,8 @@ $: showConnectionFieldArgs = { config: $config }; - $: showUser = driver?.showConnectionField('user', $values, showConnectionFieldArgs) && $values.passwordMode != 'askUser'; + $: showUser = + driver?.showConnectionField('user', $values, showConnectionFieldArgs) && $values.passwordMode != 'askUser'; $: showPassword = driver?.showConnectionField('password', $values, showConnectionFieldArgs) && $values.passwordMode != 'askPassword' && @@ -83,17 +84,19 @@ {/if} {#if $authTypes && driver?.showConnectionField('authType', $values, showConnectionFieldArgs)} - ({ - value: auth.name, - label: auth.title, - }))} - /> + {#key $authTypes} + ({ + value: auth.name, + label: auth.title, + }))} + /> + {/key} {/if} {#if driver?.showConnectionField('clientLibraryPath', $values, showConnectionFieldArgs)} diff --git a/packages/web/src/utility/api.ts b/packages/web/src/utility/api.ts index 5bfb76b1e..8cb15faf2 100644 --- a/packages/web/src/utility/api.ts +++ b/packages/web/src/utility/api.ts @@ -9,6 +9,7 @@ import { showModal } from '../modals/modalTools'; import DatabaseLoginModal, { isDatabaseLoginVisible } from '../modals/DatabaseLoginModal.svelte'; import _ from 'lodash'; import uuidv1 from 'uuid/v1'; +import { openWebLink } from './exportFileTools'; export const strmid = uuidv1(); @@ -63,7 +64,9 @@ function processApiResponse(route, args, resp) { // } if (resp?.missingCredentials) { - if (!isDatabaseLoginVisible()) { + if (resp.detail.redirectToDbLogin) { + openWebLink('connections/dblogin'); + } else if (!isDatabaseLoginVisible()) { showModal(DatabaseLoginModal, resp.detail); } return null; diff --git a/plugins/dbgate-plugin-mssql/package.json b/plugins/dbgate-plugin-mssql/package.json index 51d1d9efd..47925b83f 100644 --- a/plugins/dbgate-plugin-mssql/package.json +++ b/plugins/dbgate-plugin-mssql/package.json @@ -31,12 +31,13 @@ "plugout": "dbgate-plugout dbgate-plugin-mssql" }, "devDependencies": { + "async-lock": "^1.2.6", + "@azure/msal-node": "^2.12.0", "dbgate-plugin-tools": "^1.0.7", "dbgate-query-splitter": "^4.10.1", - "webpack": "^5.91.0", - "webpack-cli": "^5.1.4", "dbgate-tools": "^5.0.0-alpha.1", "tedious": "^18.2.0", - "async-lock": "^1.2.6" + "webpack": "^5.91.0", + "webpack-cli": "^5.1.4" } -} \ No newline at end of file +} diff --git a/plugins/dbgate-plugin-mssql/src/backend/azureAuth.js b/plugins/dbgate-plugin-mssql/src/backend/azureAuth.js new file mode 100644 index 000000000..a13bb06e7 --- /dev/null +++ b/plugins/dbgate-plugin-mssql/src/backend/azureAuth.js @@ -0,0 +1,12 @@ +function getAzureAuthTypes(platformInfo) { + return null; +} + +async function azureGetRedirectAuthUrl(connection) { + return null; +} + +module.exports = { + getAzureAuthTypes, + azureGetRedirectAuthUrl, +}; diff --git a/plugins/dbgate-plugin-mssql/src/backend/driver.js b/plugins/dbgate-plugin-mssql/src/backend/driver.js index 4168015c5..681c0ad35 100644 --- a/plugins/dbgate-plugin-mssql/src/backend/driver.js +++ b/plugins/dbgate-plugin-mssql/src/backend/driver.js @@ -8,8 +8,11 @@ const AsyncLock = require('async-lock'); const nativeDriver = require('./nativeDriver'); const lock = new AsyncLock(); const { tediousConnect, tediousQueryCore, tediousReadQuery, tediousStream } = require('./tediousDriver'); +const { getAzureAuthTypes } = require('./azureAuth'); const { nativeConnect, nativeQueryCore, nativeReadQuery, nativeStream } = nativeDriver; + let requireMsnodesqlv8; +let platformInfo; const versionQuery = ` SELECT @@ -52,7 +55,14 @@ const driver = { analyserClass: MsSqlAnalyser, getAuthTypes() { - return requireMsnodesqlv8 ? windowsAuthTypes : null; + const res = []; + if (requireMsnodesqlv8) res.push(...windowsAuthTypes); + const azureAuthTypes = getAzureAuthTypes(platformInfo); + if (azureAuthTypes) res.push(...azureAuthTypes); + if (res.length > 0) { + return _.uniqBy(res, 'name'); + } + return null; }, async connect(conn) { @@ -115,12 +125,16 @@ const driver = { const { rows } = await this.query(pool, 'SELECT name FROM sys.databases order by name'); return rows; }, + getRedirectAuthUrl(connection) { + return azureGetRedirectAuthUrl(connection); + } }; driver.initialize = dbgateEnv => { if (dbgateEnv.nativeModules && dbgateEnv.nativeModules.msnodesqlv8) { requireMsnodesqlv8 = dbgateEnv.nativeModules.msnodesqlv8; } + platformInfo = dbgateEnv.platformInfo; nativeDriver.initialize(dbgateEnv); }; diff --git a/plugins/dbgate-plugin-mssql/src/frontend/driver.js b/plugins/dbgate-plugin-mssql/src/frontend/driver.js index 84a7ddba1..fe0ab05c6 100644 --- a/plugins/dbgate-plugin-mssql/src/frontend/driver.js +++ b/plugins/dbgate-plugin-mssql/src/frontend/driver.js @@ -130,7 +130,7 @@ const driver = { field ) || (field == 'trustServerCertificate' && values.authType != 'sql' && values.authType != 'sspi') || - (field == 'windowsDomain' && values.authType != 'sql' && values.authType != 'sspi'), + (field == 'windowsDomain' && values.authType != 'sql' && values.authType != 'sspi' && values.authType != 'msentra'), // (field == 'useDatabaseUrl' && values.authType != 'sql' && values.authType != 'sspi') getQuerySplitterOptions: usage => usage == 'editor' @@ -154,6 +154,13 @@ const driver = { }, ]; }, + + beforeConnectionSave: connection => { + return { + ...connection, + useRedirectDbLogin: connection.authType == 'msentra' ? 1 : 0, + }; + }, }; module.exports = driver; diff --git a/yarn.lock b/yarn.lock index 817e109d5..9ff5025b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -164,6 +164,20 @@ 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.14.0": + version "14.14.0" + resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.14.0.tgz#31a015070d5864ebcf9ebb988fcbc5c5536f22d1" + integrity sha512-OxcOk9H1/1fktHh6//VCORgSNJc2dCQObTm6JNmL824Z6iZSO6eFo/Bttxe0hETn9B+cr7gDouTQtsRq3YPuSQ== + +"@azure/msal-node@^2.12.0": + version "2.12.0" + resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-2.12.0.tgz#57ee6b6011a320046d72dc0828fec46278f2ab2c" + integrity sha512-jmk5Im5KujRA2AcyCb0awA3buV8niSrwXZs+NBJWIvxOz76RvNlusGIqi43A0h45BPUy93Qb+CPdpJn82NFTIg== + dependencies: + "@azure/msal-common" "14.14.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"