forms login

This commit is contained in:
Jan Prochazka 2022-11-26 11:21:37 +01:00
parent 9a5287725b
commit b1ae7d53b9
6 changed files with 84 additions and 20 deletions

View File

@ -8,7 +8,8 @@ const AD = require('activedirectory2').promiseWrapper;
const tokenSecret = uuidv1();
function shouldAuthorizeApi() {
return !!process.env.OAUTH_AUTH;
const logins = getLogins();
return !!process.env.OAUTH_AUTH || !!process.env.AD_URL || (!!logins && !process.env.BASIC_AUTH);
}
function unauthorizedResponse(req, res, text) {
@ -22,7 +23,7 @@ function unauthorizedResponse(req, res, text) {
}
function authMiddleware(req, res, next) {
const SKIP_AUTH_PATHS = ['/config/get', '/auth/oauth-token', 'auth/login', '/stream'];
const SKIP_AUTH_PATHS = ['/config/get', '/auth/oauth-token', '/auth/login', '/stream'];
if (!shouldAuthorizeApi()) {
return next();
@ -85,14 +86,16 @@ module.exports = {
try {
const res = await ad.authenticate(login, password);
if (!res) {
return { error: 'login failed' };
return { error: 'Login failed' };
}
return {
accessToken: jwt.sign({ login }, tokenSecret, { expiresIn: '1m' }),
};
} catch (err) {
console.log('Failed active directory authentization', err.message);
return { error: err.message };
return {
error: err.message,
};
}
}
@ -100,7 +103,7 @@ module.exports = {
if (!logins) {
return { error: 'Logins not configured' };
}
if (logins[login] == password) {
if (logins.find(x => x.login == login)?.password == password) {
return {
accessToken: jwt.sign({ login }, tokenSecret, { expiresIn: '1m' }),
};

View File

@ -42,7 +42,7 @@ function start() {
const server = http.createServer(app);
const logins = getLogins();
if (logins) {
if (logins && process.env.BASIC_AUTH) {
app.use(
basicAuth({
users: _.fromPairs(logins.map(x => [x.login, x.password])),

View File

@ -1,5 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { internalRedirectTo } from './clientAuth';
import FormButton from './forms/FormButton.svelte';
import FormPasswordField from './forms/FormPasswordField.svelte';
import FormProvider from './forms/FormProvider.svelte';
@ -22,15 +23,26 @@
<div class="box">
<div class="heading">Log In</div>
<FormProvider>
<FormTextField label="Username" name="login" />
<FormPasswordField label="Password" name="password" />
<FormTextField label="Username" name="login" autocomplete="username" />
<FormPasswordField label="Password" name="password" autocomplete="current-password" />
<div class="submit">
<FormSubmit
value="Log In"
on:click={e => {
on:click={async e => {
enableApi();
apiCall('auth/login', e.detail);
const resp = await apiCall('auth/login', e.detail);
if (resp.error) {
internalRedirectTo(`/?page=not-logged&error=${encodeURIComponent(resp.error)}`);
return;
}
const { accessToken } = resp;
if (accessToken) {
localStorage.setItem('accessToken', accessToken);
internalRedirectTo('/');
return;
}
internalRedirectTo(`/?page=not-logged`);
}}
/>
</div>

View File

@ -1,18 +1,51 @@
<script lang="ts">
import { onMount } from 'svelte';
import FormStyledButton from './buttons/FormStyledButton.svelte';
import { redirectToLogin } from './clientAuth';
onMount(() => {
const removed = document.getElementById('starting_dbgate_zero');
if (removed) removed.remove();
});
const params = new URLSearchParams(location.search);
const error = params.get('error');
function handleLogin() {
redirectToLogin(undefined, true);
}
</script>
<div class='title'>Sorry, you are not authorized to run DbGate</div>
<div class="root theme-light theme-type-light">
<div class="title">Sorry, you are not authorized to run DbGate</div>
{#if error}
<div class="error">{error}</div>
{/if}
<div class="button">
<FormStyledButton value="Log In" on:click={handleLogin} />
</div>
</div>
<style>
.root {
color: var(--theme-font-1);
}
.title {
font-size: x-large;
margin-top: 20vh;
text-align: center;
}
.error {
margin-top: 1em;
text-align: center;
}
.button {
display: flex;
justify-content: center;
margin-top: 1em;
}
</style>

View File

@ -1,4 +1,4 @@
import { apiCall, disableApi } from './utility/api';
import { apiCall, disableApi, enableApi } from './utility/api';
import { getConfig } from './utility/metadataLoaders';
export function isOauthCallback() {
@ -23,7 +23,7 @@ export function handleOauthCallback() {
}).then(authResp => {
const { accessToken } = authResp;
localStorage.setItem('accessToken', accessToken);
location.replace('/');
internalRedirectTo('/');
});
return true;
@ -42,13 +42,21 @@ export async function handleAuthOnStartup(config) {
}
}
export async function redirectToLogin(config = null) {
if (!config) config = await getConfig();
export async function redirectToLogin(config = null, force = false) {
if (!config) {
enableApi();
config = await getConfig();
}
if (config.isLoginForm) {
const index = location.pathname.lastIndexOf('/');
const loginPath = index >= 0 ? location.pathname.substring(0, index) + '/?page=login' : '/?page=login';
location.replace(loginPath);
if (!force) {
const params = new URLSearchParams(location.search);
if (params.get('page') == 'login' || params.get('page') == 'not-logged') {
return;
}
}
internalRedirectTo('/?page=login');
return;
}
if (config.oauth) {
@ -60,5 +68,12 @@ export async function redirectToLogin(config = null) {
location.origin + location.pathname
)}&state=${encodeURIComponent(state)}`
);
return;
}
}
export function internalRedirectTo(path) {
const index = location.pathname.lastIndexOf('/');
const newPath = index >= 0 ? location.pathname.substring(0, index) + path : path;
location.replace(newPath);
}

View File

@ -4,6 +4,7 @@
export let value;
export let focused = false;
export let domEditor = undefined;
export let autocomplete = 'new-password';
if (focused) onMount(() => domEditor.focus());
</script>
@ -17,5 +18,5 @@
on:click
bind:this={domEditor}
on:keydown
autocomplete="new-password"
{autocomplete}
/>