Skip to content

Commit

Permalink
WIP: Attempt at adding header auth. Ignore Settings Lissy93#981
Browse files Browse the repository at this point in the history
  • Loading branch information
toddejohnson committed Nov 17, 2023
1 parent 4813d49 commit 4aa34f6
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 10 deletions.
10 changes: 10 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const rebuild = require('./services/rebuild-app'); // A script to programmatical
const systemInfo = require('./services/system-info'); // Basic system info, for resource widget
const sslServer = require('./services/ssl-server'); // TLS-enabled web server
const corsProxy = require('./services/cors-proxy'); // Enables API requests to CORS-blocked services
const getUser = require('./services/get-user'); // Enables server side user lookup

/* Helper functions, and default config */
const printMessage = require('./services/print-message'); // Function to print welcome msg on start
Expand Down Expand Up @@ -124,6 +125,15 @@ const app = express()
res.end(JSON.stringify({ success: false, message: e }));
}
})
// GET endpoint to return user info
.use(ENDPOINTS.getUser, (req, res) => {
try {
const user = getUser(req);
res.end(JSON.stringify(user));
} catch (e) {
res.end(JSON.stringify({ success: false, message: e }));
}
})
// GET fallback endpoint
.get('*', (req, res) => res.sendFile(path.join(__dirname, 'dist', 'index.html')));

Expand Down
5 changes: 5 additions & 0 deletions services/get-user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = (req) => {
const userHeader = "Remote-User";
console.log("Running Server Side", req.headers[userHeader.toLowerCase()]); // eslint-disable-line no-console
return { "success": true, "user": req.headers[userHeader.toLowerCase()] };
};
27 changes: 17 additions & 10 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import ErrorReporting from '@/utils/ErrorReporting'; // Error reporting initial
import clickOutside from '@/directives/ClickOutside'; // Directive for closing popups, modals, etc
import { toastedOptions, tooltipOptions, language as defaultLanguage } from '@/utils/defaults';
import { initKeycloakAuth, isKeycloakEnabled } from '@/utils/KeycloakAuth';
import { initHeaderAuth, isHeaderAuthEnabled } from '@/utils/HeaderAuth';
import Keys from '@/utils/StoreMutations';

// Initialize global Vue components
Expand Down Expand Up @@ -54,18 +55,24 @@ ErrorReporting(Vue, router);
// Render function
const render = (awesome) => awesome(Dashy);

store.dispatch(Keys.INITIALIZE_CONFIG).then((thing) => console.log('main', thing));

// Mount the app, with router, store i18n and render func
const mount = () => new Vue({
store, router, render, i18n,
}).$mount('#app');

// If Keycloak not enabled, then proceed straight to the app
if (!isKeycloakEnabled()) {
mount();
} else { // Keycloak is enabled, redirect to KC login page
initKeycloakAuth()
.then(() => mount())
.catch(() => window.location.reload());
}
store.dispatch(Keys.INITIALIZE_CONFIG).then((thing) => {
console.log('main', thing);

// Keycloak is enabled, redirect to KC login page
if (isKeycloakEnabled()) {
initKeycloakAuth()
.then(() => mount())
.catch(() => window.location.reload());
} else if (isHeaderAuthEnabled()) {
initHeaderAuth()
.then(() => mount())
.catch(() => window.location.reload());
} else { // If Keycloak not enabled, then proceed straight to the app
mount();
}
});
31 changes: 31 additions & 0 deletions src/utils/ConfigSchema.json
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,37 @@
}
}
},
"enableHeaderAuth": {
"title": "Enable HeaderAuth?",
"type": "boolean",
"default": false,
"description": "If set to true, enable Header Authentication. See appConfig.auth.headerAuth"
},
"headerAuth": {
"type": "object",
"description": "Configuration for headerAuth",
"additionalProperties": false,
"required": [
"proxyWhitelist"
],
"properties": {
"userHeader": {
"title": "User Header",
"type": "string",
"description": "Header name which contains username",
"default": "REMOTE_USER"
},
"proxyWhitelist": {
"title": "Upstream Proxy Auth Trust",
"type": "array",
"description": "Upstream proxy servers to expect authenticated requests from",
"items": {
"type": "string",
"description": "IPs of upstream proxies that will be trusted"
}
}
}
},
"enableKeycloak": {
"title": "Enable Keycloak?",
"type": "boolean",
Expand Down
77 changes: 77 additions & 0 deletions src/utils/HeaderAuth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import axios from 'axios';
import sha256 from 'crypto-js/sha256';
import ConfigAccumulator from '@/utils/ConfigAccumalator';
import { cookieKeys, localStorageKeys, serviceEndpoints } from '@/utils/defaults';
import { InfoHandler, ErrorHandler, InfoKeys } from '@/utils/ErrorHandler';
import { logout, getUserState } from '@/utils/Auth';

const getAppConfig = () => {
const Accumulator = new ConfigAccumulator();
const config = Accumulator.config();
return config.appConfig || {};
};

class HeaderAuth {
constructor() {
const { auth } = getAppConfig();
const {
userHeader, proxyWhitelist,
} = auth.headerAuth;
this.userHeader = userHeader;
this.proxyWhitelist = proxyWhitelist;
this.users = auth.users;
}

/* eslint-disable class-methods-use-this */
login() {
return new Promise((resolve, reject) => {
const baseUrl = process.env.VUE_APP_DOMAIN || window.location.origin;
axios.get(`${baseUrl}${serviceEndpoints.getUser}`).then((response) => {
if (!response.data || response.data.errorMsg) {
reject(response.data.errorMsg || 'Error');
} else {
try {
this.users.forEach((user) => {
if (user.user.toLowerCase() === response.data.user.toLowerCase()) { // User found
const strAndUpper = (input) => input.toString().toUpperCase();
const sha = strAndUpper(sha256(strAndUpper(user.user) + strAndUpper(user.hash)));
document.cookie = `${cookieKeys.AUTH_TOKEN}=${sha};`;
localStorage.setItem(localStorageKeys.USERNAME, user.user);
InfoHandler(`Succesfully signed in as ${response.data.user}`, InfoKeys.AUTH);
console.log('I think we\'re good', getUserState());
resolve(response.data.user);
}
});
} catch (e) {
reject(e);
}
}
});
});
}

logout() {
logout();
}
}

export const isHeaderAuthEnabled = () => {
const { auth } = getAppConfig();
if (!auth) return false;
return auth.enableHeaderAuth || false;
};

let headerAuth;

export const initHeaderAuth = () => {
headerAuth = new HeaderAuth();
return headerAuth.login();
};

// TODO: Find where this is implemented
export const getHeaderAuth = () => {
if (!headerAuth) {
ErrorHandler("HeaderAuth not initialized, can't get instance of class");
}
return headerAuth;
};
1 change: 1 addition & 0 deletions src/utils/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ module.exports = {
rebuild: '/config-manager/rebuild',
systemInfo: '/system-info',
corsProxy: '/cors-proxy',
getUser: '/get-user',
},
/* List of built-in themes, to be displayed within the theme-switcher dropdown */
builtInThemes: [
Expand Down

0 comments on commit 4aa34f6

Please sign in to comment.