multiauth WIP

This commit is contained in:
Jan Prochazka
2024-08-07 14:47:33 +02:00
parent 42c71c1204
commit 35532b718a
2 changed files with 94 additions and 31 deletions

View File

@@ -1,7 +1,7 @@
const { getTokenSecret, getTokenLifetime } = require('./authCommon'); const { getTokenSecret, getTokenLifetime } = require('./authCommon');
const _ = require('lodash'); const _ = require('lodash');
const axios = require('axios'); const axios = require('axios');
const { getLogger } = require('dbgate-tools'); const { getLogger, getPredefinedPermissions } = require('dbgate-tools');
const AD = require('activedirectory2').promiseWrapper; const AD = require('activedirectory2').promiseWrapper;
const jwt = require('jsonwebtoken'); const jwt = require('jsonwebtoken');
@@ -12,7 +12,15 @@ class AuthProviderBase {
amoid = 'none'; amoid = 'none';
async login(login, password, options = undefined) { async login(login, password, options = undefined) {
return {}; return {
accessToken: jwt.sign(
{
amoid: this.amoid,
},
getTokenSecret(),
{ expiresIn: getTokenLifetime() }
),
};
} }
shouldAuthorizeApi() { shouldAuthorizeApi() {
@@ -57,6 +65,7 @@ class AuthProviderBase {
toJson() { toJson() {
return { return {
amoid: this.amoid, amoid: this.amoid,
workflowType: 'anonymous',
}; };
} }
} }
@@ -127,12 +136,19 @@ class OAuthProvider extends AuthProviderBase {
oauthLogout: process.env.OAUTH_LOGOUT, oauthLogout: process.env.OAUTH_LOGOUT,
}; };
} }
toJson() {
return {
...super.toJson(),
workflowType: 'redirect',
};
}
} }
class ADProvider extends AuthProviderBase { class ADProvider extends AuthProviderBase {
amoid = 'ad'; amoid = 'ad';
async login(login, password) { async login(login, password, options = undefined) {
const adConfig = { const adConfig = {
url: process.env.AD_URL, url: process.env.AD_URL,
baseDN: process.env.AD_BASEDN, baseDN: process.env.AD_BASEDN,
@@ -152,7 +168,14 @@ class ADProvider extends AuthProviderBase {
return { error: `Username ${login} not allowed to log in` }; return { error: `Username ${login} not allowed to log in` };
} }
return { return {
accessToken: jwt.sign({ login }, getTokenSecret(), { expiresIn: getTokenLifetime() }), accessToken: jwt.sign(
{
amoid: this.amoid,
login,
},
getTokenSecret(),
{ expiresIn: getTokenLifetime() }
),
}; };
} catch (e) { } catch (e) {
return { error: 'Login failed' }; return { error: 'Login failed' };
@@ -166,15 +189,29 @@ class ADProvider extends AuthProviderBase {
isLoginForm() { isLoginForm() {
return !process.env.BASIC_AUTH; return !process.env.BASIC_AUTH;
} }
toJson() {
return {
...super.toJson(),
workflowType: 'credentials',
};
}
} }
class LoginsProvider extends AuthProviderBase { class LoginsProvider extends AuthProviderBase {
amoid = 'logins'; amoid = 'logins';
async login(login, password) { async login(login, password, options = undefined) {
if (password == process.env[`LOGIN_PASSWORD_${login}`]) { if (password == process.env[`LOGIN_PASSWORD_${login}`]) {
return { return {
accessToken: jwt.sign({ login }, getTokenSecret(), { expiresIn: getTokenLifetime() }), accessToken: jwt.sign(
{
amoid: this.amoid,
login,
},
getTokenSecret(),
{ expiresIn: getTokenLifetime() }
),
}; };
} }
return { error: 'Invalid credentials' }; return { error: 'Invalid credentials' };
@@ -187,6 +224,13 @@ class LoginsProvider extends AuthProviderBase {
isLoginForm() { isLoginForm() {
return !process.env.BASIC_AUTH; return !process.env.BASIC_AUTH;
} }
toJson() {
return {
...super.toJson(),
workflowType: 'credentials',
};
}
} }
class DenyAllProvider extends AuthProviderBase { class DenyAllProvider extends AuthProviderBase {
@@ -196,9 +240,16 @@ class DenyAllProvider extends AuthProviderBase {
return true; return true;
} }
async login(login, password) { async login(login, password, options = undefined) {
return { error: 'Login not allowed' }; return { error: 'Login not allowed' };
} }
toJson() {
return {
...super.toJson(),
workflowType: 'credentials',
};
}
} }
function hasEnvLogins() { function hasEnvLogins() {

View File

@@ -30,6 +30,9 @@
$: selectedConnection = availableConnections?.find(x => x.conid == $values.databaseServer); $: selectedConnection = availableConnections?.find(x => x.conid == $values.databaseServer);
$: selectedProvider = availableProviders?.find(x => x.amoid == $values.amoid);
$: workflowType = selectedProvider?.workflowType ?? 'credentials';
async function loadAvailableServers(amoid) { async function loadAvailableServers(amoid) {
if (amoid) { if (amoid) {
availableConnections = await apiCall('storage/get-connections-for-login-page', { amoid }); availableConnections = await apiCall('storage/get-connections-for-login-page', { amoid });
@@ -80,7 +83,7 @@
/> />
{/if} {/if}
{#if !isAdminPage && availableConnections} {#if !isAdminPage && availableConnections && workflowType == 'database'}
<FormSelectField <FormSelectField
label="Database server" label="Database server"
name="databaseServer" name="databaseServer"
@@ -97,10 +100,12 @@
<FormPasswordField label="Password" name="password" autocomplete="current-password" saveOnInput /> <FormPasswordField label="Password" name="password" autocomplete="current-password" saveOnInput />
{/if} {/if}
{:else} {:else}
{#if !isAdminPage} {#if !isAdminPage && workflowType == 'credentials'}
<FormTextField label="Username" name="login" autocomplete="username" saveOnInput /> <FormTextField label="Username" name="login" autocomplete="username" saveOnInput />
{/if} {/if}
<FormPasswordField label="Password" name="password" autocomplete="current-password" saveOnInput /> {#if workflowType == 'credentials'}
<FormPasswordField label="Password" name="password" autocomplete="current-password" saveOnInput />
{/if}
{/if} {/if}
{#if isAdminPage && $config && !$config.isAdminLoginForm} {#if isAdminPage && $config && !$config.isAdminLoginForm}
@@ -173,31 +178,38 @@
/> />
{:else} {:else}
<FormSubmit <FormSubmit
value={isAdminPage ? 'Log In as Administrator' : 'Log In'} value={isAdminPage
? 'Log In as Administrator'
: workflowType == 'redirect'
? 'Redirect to login page'
: 'Log In'}
on:click={async e => { on:click={async e => {
enableApi(); enableApi();
const resp = await apiCall('auth/login', {
amoid: $values.amoid, if (isAdminPage || workflowType == 'credentials' || workflowType == 'anonymous') {
isAdminPage, const resp = await apiCall('auth/login', {
...e.detail, amoid: $values.amoid,
}); isAdminPage,
if (resp.error) { ...e.detail,
internalRedirectTo( });
`/?page=not-logged&error=${encodeURIComponent(resp.error)}&is-admin=${isAdminPage ? 'true' : ''}` if (resp.error) {
); internalRedirectTo(
return; `/?page=not-logged&error=${encodeURIComponent(resp.error)}&is-admin=${isAdminPage ? 'true' : ''}`
} );
const { accessToken } = resp; return;
if (accessToken) {
localStorage.setItem(isAdminPage ? 'adminAccessToken' : 'accessToken', accessToken);
if (isAdminPage) {
internalRedirectTo('/?page=admin');
} else {
internalRedirectTo('/');
} }
return; const { accessToken } = resp;
if (accessToken) {
localStorage.setItem(isAdminPage ? 'adminAccessToken' : 'accessToken', accessToken);
if (isAdminPage) {
internalRedirectTo('/?page=admin');
} else {
internalRedirectTo('/');
}
return;
}
internalRedirectTo(`/?page=not-logged`);
} }
internalRedirectTo(`/?page=not-logged`);
}} }}
/> />
{/if} {/if}