mirror of
https://github.com/DeNNiiInc/dbgate.git
synced 2026-05-01 03:53:57 +00:00
multiauth refactor
This commit is contained in:
@@ -23,10 +23,6 @@ class AuthProviderBase {
|
||||
};
|
||||
}
|
||||
|
||||
shouldAuthorizeApi() {
|
||||
return false;
|
||||
}
|
||||
|
||||
oauthToken(params) {
|
||||
return {};
|
||||
}
|
||||
@@ -46,14 +42,6 @@ class AuthProviderBase {
|
||||
return permissions || process.env.PERMISSIONS;
|
||||
}
|
||||
|
||||
isLoginForm() {
|
||||
return false;
|
||||
}
|
||||
|
||||
getAdditionalConfigProps() {
|
||||
return {};
|
||||
}
|
||||
|
||||
getLoginPageConnections() {
|
||||
return null;
|
||||
}
|
||||
@@ -68,15 +56,17 @@ class AuthProviderBase {
|
||||
workflowType: 'anonymous',
|
||||
};
|
||||
}
|
||||
|
||||
redirect({ state }) {
|
||||
return {
|
||||
status: 'error',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class OAuthProvider extends AuthProviderBase {
|
||||
amoid = 'oauth';
|
||||
|
||||
shouldAuthorizeApi() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async oauthToken(params) {
|
||||
const { redirectUri, code } = params;
|
||||
|
||||
@@ -143,6 +133,18 @@ class OAuthProvider extends AuthProviderBase {
|
||||
workflowType: 'redirect',
|
||||
};
|
||||
}
|
||||
|
||||
redirect({ state, redirectUri }) {
|
||||
const scopeParam = process.env.OAUTH_SCOPE ? `&scope=${process.env.OAUTH_SCOPE}` : '';
|
||||
return {
|
||||
status: 'ok',
|
||||
uri: `${process.env.OAUTH_AUTH}?client_id=${
|
||||
process.env.OAUTH_CLIENT_ID
|
||||
}&response_type=code&redirect_uri=${encodeURIComponent(redirectUri)}&state=${encodeURIComponent(
|
||||
state
|
||||
)}${scopeParam}`,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ADProvider extends AuthProviderBase {
|
||||
@@ -182,14 +184,6 @@ class ADProvider extends AuthProviderBase {
|
||||
}
|
||||
}
|
||||
|
||||
shouldAuthorizeApi() {
|
||||
return !process.env.BASIC_AUTH;
|
||||
}
|
||||
|
||||
isLoginForm() {
|
||||
return !process.env.BASIC_AUTH;
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
...super.toJson(),
|
||||
@@ -217,14 +211,6 @@ class LoginsProvider extends AuthProviderBase {
|
||||
return { error: 'Invalid credentials' };
|
||||
}
|
||||
|
||||
shouldAuthorizeApi() {
|
||||
return !process.env.BASIC_AUTH;
|
||||
}
|
||||
|
||||
isLoginForm() {
|
||||
return !process.env.BASIC_AUTH;
|
||||
}
|
||||
|
||||
toJson() {
|
||||
return {
|
||||
...super.toJson(),
|
||||
@@ -236,10 +222,6 @@ class LoginsProvider extends AuthProviderBase {
|
||||
class DenyAllProvider extends AuthProviderBase {
|
||||
amoid = 'deny';
|
||||
|
||||
shouldAuthorizeApi() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async login(login, password, options = undefined) {
|
||||
return { error: 'Login not allowed' };
|
||||
}
|
||||
|
||||
@@ -5,7 +5,12 @@ const { getLogger } = require('dbgate-tools');
|
||||
const AD = require('activedirectory2').promiseWrapper;
|
||||
const crypto = require('crypto');
|
||||
const { getTokenSecret, getTokenLifetime } = require('../auth/authCommon');
|
||||
const { getAuthProviderFromReq, getAuthProviders, getDefaultAuthProvider, getAuthProviderById } = require('../auth/authProvider');
|
||||
const {
|
||||
getAuthProviderFromReq,
|
||||
getAuthProviders,
|
||||
getDefaultAuthProvider,
|
||||
getAuthProviderById,
|
||||
} = require('../auth/authProvider');
|
||||
const storage = require('./storage');
|
||||
|
||||
const logger = getLogger('auth');
|
||||
@@ -27,6 +32,7 @@ function authMiddleware(req, res, next) {
|
||||
'/config/get-settings',
|
||||
'/auth/oauth-token',
|
||||
'/auth/login',
|
||||
'/auth/redirect',
|
||||
'/stream',
|
||||
'storage/get-connections-for-login-page',
|
||||
'auth/get-providers',
|
||||
@@ -37,11 +43,13 @@ function authMiddleware(req, res, next) {
|
||||
|
||||
// console.log('********************* getAuthProvider()', getAuthProvider());
|
||||
|
||||
const isAdminPage = req.headers['x-is-admin-page'] == 'true';
|
||||
// const isAdminPage = req.headers['x-is-admin-page'] == 'true';
|
||||
|
||||
if (!isAdminPage && !getAuthProviderFromReq(req).shouldAuthorizeApi()) {
|
||||
if (process.env.BASIC_AUTH) {
|
||||
// API is not authorized for basic auth
|
||||
return next();
|
||||
}
|
||||
|
||||
let skipAuth = !!SKIP_AUTH_PATHS.find(x => req.path == getExpressPath(x));
|
||||
|
||||
const authHeader = req.headers.authorization;
|
||||
@@ -70,7 +78,8 @@ function authMiddleware(req, res, next) {
|
||||
module.exports = {
|
||||
oauthToken_meta: true,
|
||||
async oauthToken(params) {
|
||||
return getDefaultAuthProvider().oauthToken(params);
|
||||
const { amoid } = params;
|
||||
return getAuthProviderById(amoid).oauthToken(params);
|
||||
},
|
||||
login_meta: true,
|
||||
async login(params) {
|
||||
@@ -107,5 +116,11 @@ module.exports = {
|
||||
};
|
||||
},
|
||||
|
||||
redirect_meta: true,
|
||||
async redirect(params) {
|
||||
const { amoid } = params;
|
||||
return getAuthProviderById(amoid).redirect(params);
|
||||
},
|
||||
|
||||
authMiddleware,
|
||||
};
|
||||
|
||||
@@ -31,8 +31,6 @@ module.exports = {
|
||||
const authProvider = getAuthProviderFromReq(req);
|
||||
const login = authProvider.getCurrentLogin(req);
|
||||
const permissions = authProvider.getCurrentPermissions(req);
|
||||
const isLoginForm = authProvider.isLoginForm();
|
||||
const additionalConfigProps = authProvider.getAdditionalConfigProps();
|
||||
const isUserLoggedIn = authProvider.isUserLoggedIn(req);
|
||||
|
||||
const singleConid = authProvider.getSingleConnectionId(req);
|
||||
@@ -52,12 +50,17 @@ module.exports = {
|
||||
isDocker: platformInfo.isDocker,
|
||||
isElectron: platformInfo.isElectron,
|
||||
isLicenseValid: platformInfo.isLicenseValid,
|
||||
licenseError: platformInfo.licenseError,
|
||||
checkedLicense: platformInfo.checkedLicense,
|
||||
permissions,
|
||||
login,
|
||||
...additionalConfigProps,
|
||||
isLoginForm,
|
||||
isAdminLoginForm: !!(process.env.STORAGE_DATABASE && process.env.ADMIN_PASSWORD && !process.env.BASIC_AUTH),
|
||||
// ...additionalConfigProps,
|
||||
isBasicAuth: !!process.env.BASIC_AUTH,
|
||||
isAdminLoginForm: !!(
|
||||
process.env.STORAGE_DATABASE &&
|
||||
process.env.ADMIN_PASSWORD &&
|
||||
!process.env.BASIC_AUTH &&
|
||||
platformInfo.checkedLicense?.type == 'premium'
|
||||
),
|
||||
storageDatabase: process.env.STORAGE_DATABASE,
|
||||
logsFilePath: getLogsFilePath(),
|
||||
connectionsFilePath: path.join(datadir(), 'connections.jsonl'),
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
function checkLicense() {
|
||||
return null;
|
||||
return {
|
||||
status: 'ok',
|
||||
type: 'community',
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -13,8 +13,7 @@ const isDocker = fs.existsSync('/home/dbgate-docker/public');
|
||||
const isDevMode = process.env.DEVMODE == '1';
|
||||
const isNpmDist = !!global['IS_NPM_DIST'];
|
||||
const isForkedApi = processArgs.isForkedApi;
|
||||
const licenseError = checkLicense();
|
||||
const isLicenseValid = licenseError == null;
|
||||
const checkedLicense = checkLicense();
|
||||
|
||||
// function moduleAvailable(name) {
|
||||
// try {
|
||||
@@ -33,8 +32,8 @@ const platformInfo = {
|
||||
isElectronBundle: isElectron() && !isDevMode,
|
||||
isForkedApi,
|
||||
isElectron: isElectron(),
|
||||
isLicenseValid,
|
||||
licenseError,
|
||||
checkedLicense,
|
||||
isLicenseValid: checkedLicense?.status == 'ok',
|
||||
isDevMode,
|
||||
isNpmDist,
|
||||
isSnap: process.env.ELECTRON_SNAP == 'true',
|
||||
|
||||
@@ -24,9 +24,9 @@
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="heading">Configuration error</div>
|
||||
{#if $config?.isLicenseValid == false}
|
||||
{#if $config?.checkedLicense?.status == 'error'}
|
||||
<ErrorInfo
|
||||
message={`Invalid license. Please contact sales@dbgate.eu for more details. ${$config?.licenseError}`}
|
||||
message={`Invalid license. Please contact sales@dbgate.eu for more details. ${$config?.checkedLicense?.error}`}
|
||||
/>
|
||||
{:else if error}
|
||||
<ErrorInfo message={error} />
|
||||
|
||||
@@ -209,6 +209,22 @@
|
||||
return;
|
||||
}
|
||||
internalRedirectTo(`/?page=not-logged`);
|
||||
} else if (workflowType == 'redirect') {
|
||||
const state = `dbg-oauth:${strmid}:${$values.amoid}`;
|
||||
|
||||
sessionStorage.setItem('oauthState', state);
|
||||
console.log('Redirecting to OAUTH provider');
|
||||
|
||||
const resp = await apiCall('auth/redirect', {
|
||||
amoid: $values.amoid,
|
||||
state,
|
||||
redirectUri: location.origin + location.pathname,
|
||||
});
|
||||
|
||||
const { uri } = resp;
|
||||
if (uri) {
|
||||
location.replace(uri);
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { apiCall, enableApi } from './utility/api';
|
||||
import { apiCall, enableApi, getAuthCategory } from './utility/api';
|
||||
import { getConfig } from './utility/metadataLoaders';
|
||||
import { isAdminPage } from './utility/pageDefs';
|
||||
|
||||
@@ -40,9 +40,12 @@ export function handleOauthCallback() {
|
||||
const sentCode = params.get('code');
|
||||
|
||||
if (isOauthCallback()) {
|
||||
const [_prefix, strmid, amoid] = sessionStorage.getItem('oauthState').split(':');
|
||||
|
||||
sessionStorage.removeItem('oauthState');
|
||||
apiCall('auth/oauth-token', {
|
||||
code: sentCode,
|
||||
amoid,
|
||||
redirectUri: location.origin + location.pathname,
|
||||
}).then(authResp => {
|
||||
const { accessToken, error, errorMessage } = authResp;
|
||||
@@ -113,7 +116,7 @@ export async function handleAuthOnStartup(config, isAdminPage = false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.isAdminLoginForm && isAdminPage) {
|
||||
if (getAuthCategory(config) == 'admin') {
|
||||
if (localStorage.getItem('adminAccessToken')) {
|
||||
return;
|
||||
}
|
||||
@@ -122,10 +125,10 @@ export async function handleAuthOnStartup(config, isAdminPage = false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.oauth) {
|
||||
console.log('OAUTH callback URL:', location.origin + location.pathname);
|
||||
}
|
||||
if (config.oauth || config.isLoginForm) {
|
||||
// if (config.oauth) {
|
||||
// console.log('OAUTH callback URL:', location.origin + location.pathname);
|
||||
// }
|
||||
if (getAuthCategory(config) == 'token') {
|
||||
if (localStorage.getItem('accessToken')) {
|
||||
return;
|
||||
}
|
||||
@@ -145,7 +148,7 @@ export async function redirectToLogin(config = null, force = false) {
|
||||
config = await getConfig();
|
||||
}
|
||||
|
||||
if (config.isLoginForm) {
|
||||
if (getAuthCategory(config) == 'token') {
|
||||
if (!force) {
|
||||
const params = new URLSearchParams(location.search);
|
||||
if (params.get('page') == 'login' || params.get('page') == 'admin-login' || params.get('page') == 'not-logged') {
|
||||
|
||||
@@ -12,6 +12,7 @@ import uuidv1 from 'uuid/v1';
|
||||
import { openWebLink } from './exportFileTools';
|
||||
import { callServerPing } from './connectionsPinger';
|
||||
import { batchDispatchCacheTriggers, dispatchCacheChange } from './cache';
|
||||
import { isAdminPage } from './pageDefs';
|
||||
|
||||
export const strmid = uuidv1();
|
||||
|
||||
@@ -251,6 +252,19 @@ export function installNewVolatileConnectionListener() {
|
||||
});
|
||||
}
|
||||
|
||||
export function getAuthCategory(config) {
|
||||
if (config.isBasicAuth) {
|
||||
return 'basic';
|
||||
}
|
||||
if (isAdminPage() && config.isAdminLoginForm) {
|
||||
return 'admin';
|
||||
}
|
||||
if (getElectron()) {
|
||||
return 'electron';
|
||||
}
|
||||
return 'token';
|
||||
}
|
||||
|
||||
function enableApiLog() {
|
||||
apiLogging = true;
|
||||
console.log('API loggin enabled');
|
||||
|
||||
@@ -21,8 +21,8 @@ export function resolveApiHeaders() {
|
||||
if (accessToken) {
|
||||
res['Authorization'] = `Bearer ${accessToken}`;
|
||||
}
|
||||
if (isAdminPage()) {
|
||||
res['x-is-admin-page'] = 'true';
|
||||
}
|
||||
// if (isAdminPage()) {
|
||||
// res['x-is-admin-page'] = 'true';
|
||||
// }
|
||||
return res;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user