Skip to content

Commit

Permalink
Migrating easy to move functions out of the authenticated middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
codyrancher committed May 23, 2024
1 parent 2f6d348 commit a193b53
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 131 deletions.
140 changes: 11 additions & 129 deletions shell/middleware/authenticated.js
Original file line number Diff line number Diff line change
@@ -1,86 +1,21 @@
import { NAME as EXPLORER } from '@shell/config/product/explorer';
import { SETUP, TIMED_OUT } from '@shell/config/query-params';
import { SETUP } from '@shell/config/query-params';
import { SETTING } from '@shell/config/settings';
import { MANAGEMENT, NORMAN, DEFAULT_WORKSPACE } from '@shell/config/types';
import { applyProducts } from '@shell/store/type-map';
import { findBy } from '@shell/utils/array';
import { ClusterNotFoundError, RedirectToError } from '@shell/utils/error';
import { get } from '@shell/utils/object';
import dynamicPluginLoader from '@shell/pkg/dynamic-plugin-loader';
import { AFTER_LOGIN_ROUTE, WORKSPACE } from '@shell/store/prefs';
import { BACK_TO } from '@shell/config/local-storage';
import { NAME as FLEET_NAME } from '@shell/config/product/fleet.js';
import { canViewResource } from '@shell/utils/auth';
import { getClusterFromRoute, getProductFromRoute, getPackageFromRoute, getResourceFromRoute } from '@shell/utils/router';
import {
validateResource, setProduct, isLoggedIn, notLoggedIn, noAuth, tryInitialSetup, findMe
} from '@shell/utils/auth';
import { getClusterFromRoute, getProductFromRoute, getPackageFromRoute } from '@shell/utils/router';
import { fetchInitialSettings } from '@shell/utils/settings';

let beforeEachSetup = false;

function setProduct(store, to) {
let product = getProductFromRoute(to);

// since all products are hardcoded as routes (ex: c-local-explorer), if we match the wildcard route it means that the product does not exist
if ((product && (!to.matched.length || (to.matched.length && to.matched[0].path === '/c/:cluster/:product'))) ||
// if the product grabbed from the route is not registered, then we don't have it!
(product && !store.getters['type-map/isProductRegistered'](product))) {
const error = new Error(store.getters['i18n/t']('nav.failWhale.productNotFound', { productNotFound: product }, true));

store.dispatch('loadingError', error);

throw new Error('loadingError', new Error(store.getters['i18n/t']('nav.failWhale.productNotFound', { productNotFound: product }, true)));
}

if ( !product ) {
product = EXPLORER;
}

const oldProduct = store.getters['productId'];
const oldStore = store.getters['currentProduct']?.inStore;

if ( product !== oldProduct ) {
store.commit('setProduct', product);
}

const neuStore = store.getters['currentProduct']?.inStore;

if ( neuStore !== oldStore ) {
// If the product store changes, clear the catalog.
// There might be management catalog items in it vs cluster.
store.commit('catalog/reset');
}
}

/**
* Check that the resource is valid, if not redirect to fail whale
*
* This requires that
* - product is set
* - product's store is set and setup (so we can check schema's within it)
* - product's store has the schemaFor getter (extension stores might not have it)
* - there's a resource associated with route (meta or param)
*/
function validateResource(store, to) {
const product = store.getters['currentProduct'];
const resource = getResourceFromRoute(to);

// In order to check a resource is valid we need these
if (!product || !resource) {
return false;
}

if (canViewResource(store, resource)) {
return false;
}

// Unknown resource, redirect to fail whale

const error = new Error(store.getters['i18n/t']('nav.failWhale.resourceNotFound', { resource }, true));

store.dispatch('loadingError', error);

throw error;
}

export default async function({
route, store, redirect, from, $plugin, next
}) {
Expand Down Expand Up @@ -141,26 +76,6 @@ export default async function({
}
}

// Make sure you're actually logged in
function isLoggedIn(me) {
store.commit('auth/hasAuth', true);
store.commit('auth/loggedInAs', me.id);
}

function notLoggedIn() {
store.commit('auth/hasAuth', true);

if ( route.name === 'index' ) {
return redirect(302, '/auth/login');
} else {
return redirect(302, `/auth/login?${ TIMED_OUT }`);
}
}

function noAuth() {
store.commit('auth/hasAuth', false);
}

if ( store.getters['auth/enabled'] !== false && !store.getters['auth/loggedIn'] ) {
// `await` so we have one successfully request whilst possibly logged in (ensures fromHeader is populated from `x-api-cattle-auth`)
await store.dispatch('auth/getUser');
Expand All @@ -175,29 +90,29 @@ export default async function({
const fromHeader = store.getters['auth/fromHeader'];

if ( fromHeader === 'none' ) {
noAuth();
noAuth(store);
} else if ( fromHeader === 'true' ) {
const me = await findMe(store);

isLoggedIn(me);
isLoggedIn(store, me);
} else if ( fromHeader === 'false' ) {
notLoggedIn();
notLoggedIn(store, redirect, route);

return;
} else {
// Older versions look at principals and see what happens
try {
const me = await findMe(store);

isLoggedIn(me);
isLoggedIn(store, me);
} catch (e) {
const status = e?._status;

if ( status === 404 ) {
noAuth();
noAuth(store);
} else {
if ( status === 401 ) {
notLoggedIn();
notLoggedIn(store, redirect, route);
} else {
store.commit('setError', { error: e, locationError: new Error('Auth Middleware') });
}
Expand Down Expand Up @@ -380,36 +295,3 @@ export default async function({
}
}
}

async function findMe(store) {
// First thing we do in loadManagement is fetch principals anyway.... so don't ?me=true here
const principals = await store.dispatch('rancher/findAll', {
type: NORMAN.PRINCIPAL,
opt: {
url: '/v3/principals',
redirectUnauthorized: false,
}
});

const me = findBy(principals, 'me', true);

return me;
}

async function tryInitialSetup(store, password = 'admin') {
try {
const res = await store.dispatch('auth/login', {
provider: 'local',
body: {
username: 'admin',
password
},
});

return res._status === 200;
} catch (e) {
console.error('Error trying initial setup', e); // eslint-disable-line no-console

return false;
}
}
152 changes: 150 additions & 2 deletions shell/utils/auth.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { Popup, popupWindowOptions } from '@shell/utils/window';
import { parse as parseUrl, addParam } from '@shell/utils/url';
import { BACK_TO, SPA, _EDIT, _FLAGGED } from '@shell/config/query-params';
import { MANAGEMENT } from '@shell/config/types';
import {
BACK_TO, SPA, _EDIT, _FLAGGED, TIMED_OUT
} from '@shell/config/query-params';
import { MANAGEMENT, NORMAN } from '@shell/config/types';
import { allHash } from '@shell/utils/promise';
import { getProductFromRoute, getResourceFromRoute } from '@shell/utils/router';
import { NAME as EXPLORER } from '@shell/config/product/explorer';
import { findBy } from '@shell/utils/array';

export function openAuthPopup(url, provider) {
const popup = new Popup(() => {
Expand Down Expand Up @@ -161,3 +166,146 @@ export const canViewResource = (store, resource) => {

return !!validResource;
};

// ************************************************************
//
// BELOW ARE METHODS THAT ARE A PART OF THE AUTHENTICATED MIDDLEWARE REMOVAL. THIS IS A TEMPORARY HOME FOR THESE UTILS AND SHOULD BE REWRITTEN, MOVED OR DELETED.
//
// TODO: Remove and refactor everything below for more clarity and better organization.
//
// ************************************************************

/**
* Attempt to set the product in our datastore if the route matches a known product. Otherwise show an error page instead.
*/
export function setProduct(store, to) {
let product = getProductFromRoute(to);

// since all products are hardcoded as routes (ex: c-local-explorer), if we match the wildcard route it means that the product does not exist
if ((product && (!to.matched.length || (to.matched.length && to.matched[0].path === '/c/:cluster/:product'))) ||
// if the product grabbed from the route is not registered, then we don't have it!
(product && !store.getters['type-map/isProductRegistered'](product))) {
const error = new Error(store.getters['i18n/t']('nav.failWhale.productNotFound', { productNotFound: product }, true));

store.dispatch('loadingError', error);

throw new Error('loadingError', new Error(store.getters['i18n/t']('nav.failWhale.productNotFound', { productNotFound: product }, true)));
}

if ( !product ) {
product = EXPLORER;
}

const oldProduct = store.getters['productId'];
const oldStore = store.getters['currentProduct']?.inStore;

if ( product !== oldProduct ) {
store.commit('setProduct', product);
}

const neuStore = store.getters['currentProduct']?.inStore;

if ( neuStore !== oldStore ) {
// If the product store changes, clear the catalog.
// There might be management catalog items in it vs cluster.
store.commit('catalog/reset');
}
}

/**
* Check that the resource is valid, if not redirect to fail whale
*
* This requires that
* - product is set
* - product's store is set and setup (so we can check schema's within it)
* - product's store has the schemaFor getter (extension stores might not have it)
* - there's a resource associated with route (meta or param)
*/
export function validateResource(store, to) {
const product = store.getters['currentProduct'];
const resource = getResourceFromRoute(to);

// In order to check a resource is valid we need these
if (!product || !resource) {
return false;
}

if (canViewResource(store, resource)) {
return false;
}

// Unknown resource, redirect to fail whale

const error = new Error(store.getters['i18n/t']('nav.failWhale.resourceNotFound', { resource }, true));

store.dispatch('loadingError', error);

throw error;
}

/**
* Attempt to load the current user's principal
*/
export async function findMe(store) {
// First thing we do in loadManagement is fetch principals anyway.... so don't ?me=true here
const principals = await store.dispatch('rancher/findAll', {
type: NORMAN.PRINCIPAL,
opt: {
url: '/v3/principals',
redirectUnauthorized: false,
}
});

const me = findBy(principals, 'me', true);

return me;
}

/**
* Attempt to login with default credentials. Note: I think that this may actually be outdated since we don't use these default credentials anymore on setup.
*/
export async function tryInitialSetup(store, password = 'admin') {
try {
const res = await store.dispatch('auth/login', {
provider: 'local',
body: {
username: 'admin',
password
},
});

return res._status === 200;
} catch (e) {
console.error('Error trying initial setup', e); // eslint-disable-line no-console

return false;
}
}

/**
* Record in our state management that we're indeed logged in
*/
export function isLoggedIn(store, me) {
store.commit('auth/hasAuth', true);
store.commit('auth/loggedInAs', me.id);
}

/**
* Record in our state management that we're not logged in and then redirect to the login page
*/
export function notLoggedIn(store, redirect, route) {
store.commit('auth/hasAuth', true);

if ( route.name === 'index' ) {
return redirect(302, '/auth/login');
} else {
return redirect(302, `/auth/login?${ TIMED_OUT }`);
}
}

/**
* Record in our state management that we don't have any auth providers
*/
export function noAuth(store) {
store.commit('auth/hasAuth', false);
}

0 comments on commit a193b53

Please sign in to comment.