Skip to content

Commit

Permalink
Completing implementation of the authentication and logout process.
Browse files Browse the repository at this point in the history
  • Loading branch information
krulis-martin committed Jan 2, 2025
1 parent 1c7409d commit 8d01a7f
Show file tree
Hide file tree
Showing 15 changed files with 108 additions and 110 deletions.
7 changes: 4 additions & 3 deletions etc/env.json.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"PORT": 8080,
"API_BASE": "https://recodex.base.domain/api",
"URL_PATH_PREFIX": "",
"API_BASE": "https://siscodex.base.domain/api",
"RECODEX_URI:": "https://recodex.webapp.domain",
"TITLE": "SIS-CodEx",
"SKIN": "success",
"URL_PATH_PREFIX": "",
"PERSISTENT_TOKENS_KEY_PREFIX": "rex-sis-devel"
"PERSISTENT_TOKENS_KEY_PREFIX": "recodex-sis"
}
1 change: 1 addition & 0 deletions etc/env.json.local
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"PORT": 8084,
"API_BASE": "http://127.0.0.1:4004",
"RECODEX_URI:": "https://recodex-devel.ms.mff.cuni.cz/",
"TITLE": "SIS-CodEx",
"SKIN": "warning",
"URL_PATH_PREFIX": "",
Expand Down
2 changes: 2 additions & 0 deletions src/components/icons/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const NoteIcon = props => <Icon {...props} icon={['far', 'sticky-note']}
export const ObserverIcon = props => <Icon {...props} icon="binoculars" />;
export const OutputIcon = props => <Icon {...props} icon="sign-out-alt" />;
export const PastDeadlineIcon = props => <Icon {...props} icon="skull" />;
export const PersonalDataIcon = props => <Icon {...props} icon="id-card" />;
export const PipelineIcon = props => <Icon {...props} icon="random" />;
export const PipelineStructureIcon = props => <Icon {...props} icon="sitemap" />;
export const PlagiarismIcon = props => <Icon icon="person-circle-exclamation" {...props} />;
Expand All @@ -93,6 +94,7 @@ export const RefreshIcon = props => <Icon {...props} icon="sync" />;
export const RemoveIcon = props => <Icon {...props} icon="minus-circle" />;
export const RemoveUserIcon = props => <Icon {...props} icon="user-slash" />;
export const ResultsIcon = props => <Icon {...props} icon="chart-line" />;
export const ReturnIcon = props => <Icon {...props} icon="person-walking-arrow-loop-left" />;
export const ReviewIcon = ({ review = null, reviewRequest = false, ...props }) =>
review && review.closedAt ? (
<Icon {...props} icon={review.issues > 0 ? 'file-circle-exclamation' : 'file-circle-check'} />
Expand Down
52 changes: 23 additions & 29 deletions src/components/layout/Sidebar/Sidebar.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,13 @@ import { Link } from 'react-router-dom';

import UserPanelContainer from '../../../containers/UserPanelContainer';

import MenuTitle from '../../widgets/Sidebar/MenuTitle';
import MenuItem from '../../widgets/Sidebar/MenuItem';
import { LoadingIcon } from '../../icons';
import { getJsData } from '../../../redux/helpers/resourceManager';
import { isSupervisorRole, isSuperadminRole } from '../../helpers/usersRoles.js';
import withLinks from '../../../helpers/withLinks.js';
import { getConfigVar } from '../../../helpers/config.js';
import { getReturnUrl } from '../../../helpers/localStorage.js';

import './sidebar.css';

Expand Down Expand Up @@ -55,37 +55,31 @@ const Sidebar = ({ pendingFetchOperations, loggedInUser, currentUrl, links: { HO
<div className="sidebar-wrapper">
<div data-overlayscrollbars-viewport="scrollbarHidden">
<nav className="mt-2">
{!user && (
<ul
className="nav nav-pills sidebar-menu flex-column"
data-lte-toggle="treeview"
role="menu"
data-accordion="false">
<MenuTitle title="ReCodEx" />
<MenuItem
title={<FormattedMessage id="app.sidebar.menu.signIn" defaultMessage="Sign in" />}
icon="sign-in-alt"
currentPath={currentUrl}
link={HOME_URI}
/>
</ul>
)}

{Boolean(user) && (
<>
<ul
className="nav nav-pills sidebar-menu flex-column"
data-lte-toggle="treeview"
role="menu"
data-accordion="false">
<MenuTitle title={<FormattedMessage id="app.sidebar.menu.title" defaultMessage="Menu" />} />
<MenuItem title="TODO" icon="tachometer-alt" currentPath={currentUrl} link="TODO" />
<ul
className="nav nav-pills sidebar-menu flex-column"
data-lte-toggle="treeview"
role="menu"
data-accordion="false">
{user ? (
<>
<MenuItem
title={<FormattedMessage id="app.sidebar.menu.user" defaultMessage="Personal Data" />}
icon="id-card"
currentPath={currentUrl}
link="TODO"
/>

{isSupervisorRole(user.role) && <></>}
{isSuperadminRole(user.role) && <></>}
</ul>
</>
)}
</>
) : (
<MenuItem
title={<FormattedMessage id="app.sidebar.menu.return" defaultMessage="Back to ReCodEx" />}
icon="person-walking-arrow-loop-left"
link={getReturnUrl()}
/>
)}
</ul>
</nav>
</div>
</div>
Expand Down
12 changes: 10 additions & 2 deletions src/components/widgets/Sidebar/UserPanel/UserPanel.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { FormattedMessage, FormattedRelativeTime } from 'react-intl';
import { Tooltip, OverlayTrigger } from 'react-bootstrap';

import UserName from '../../../Users/UsersName';
import Icon from '../../../icons';
import { ReturnIcon } from '../../../icons';
import { getReturnUrl, setReturnUrl } from '../../../../helpers/localStorage';

import './userPanel.css';

Expand Down Expand Up @@ -37,8 +38,15 @@ class UserPanel extends Component {
onClick={e => {
e.preventDefault();
logout();

// let's go back to ReCodEx after the logout...
const url = getReturnUrl();
if (url && window) {
setReturnUrl(null);
window.location.assign(url);
}
}}>
<Icon icon="sign-out-alt" className="text-danger sidebar-up-collapse-gaps" gapLeft={2} gapRight={1} />
<ReturnIcon className="text-danger sidebar-up-collapse-gaps" gapRight />
<span className="sidebar-up-hide-collapsed">
<FormattedMessage id="app.logout" defaultMessage="Logout" />
</span>
Expand Down
15 changes: 2 additions & 13 deletions src/containers/App/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,10 @@ import { addNotification } from '../../redux/modules/notifications.js';
import { logout, refresh } from '../../redux/modules/auth.js';
import { resourceStatus } from '../../redux/helpers/resourceManager';
import { suspendAbortPendingRequestsOptimization } from '../../pages/routes.js';
import { LoadingIcon } from '../../components/icons';
import { SESSION_EXPIRED_MESSAGE } from '../../redux/helpers/api/tools.js';
import withRouter, { withRouterProps } from '../../helpers/withRouter.js';

import './recodex.css';
import './siscodex.css';

library.add(regularIcons, solidIcons, brandIcons);

Expand Down Expand Up @@ -94,17 +93,7 @@ class App extends Component {
};

render() {
const { userId } = this.props;
return userId ? (
<div
style={{
textAlign: 'center',
height: '100vh',
lineHeight: '100vh',
}}>
<LoadingIcon size="3x" />
</div>
) : (
return (
<Routes>
<Route path="*" element={<LayoutContainer />} />
</Routes>
Expand Down
File renamed without changes.
13 changes: 13 additions & 0 deletions src/helpers/localStorage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { getConfigVar } from './config.js';

const PERSISTENT_TOKENS_KEY_PREFIX = (getConfigVar('PERSISTENT_TOKENS_KEY_PREFIX') || 'rex-sis') + '/';
const RECODEX_URI = getConfigVar('RECODEX_URI') || null;

/**
* Test whether local storage is available to us (i.e., we are on a client).
Expand Down Expand Up @@ -127,3 +128,15 @@ export const removeListener = id => {
}
}
};

/**
* Get return URL for ReCodEx web app
* @returns {string|null} URL or null
*/
export const getReturnUrl = () => storageGetItem('returnUrl') || RECODEX_URI;

/**
* Get return URL for ReCodEx web app
* @returns {string|null} URL or null
*/
export const setReturnUrl = url => (url ? storageSetItem('returnUrl', url) : storageRemoveItem('returnUrl'));
23 changes: 6 additions & 17 deletions src/locales/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
"app.groups.removeFromGroup": "Odebrat ze skupiny",
"app.header.languageSwitching.translationTitle": "Jazyková verze",
"app.headerNotification.copiedToClippboard": "Zkopírováno do schránky.",
"app.homepage.processingToken": "Probíhá zpracování přihlašovacího tokenu...",
"app.homepage.processingTokenFailed": "Autentizační proces selhal.",
"app.homepage.returnToReCodEx": "Návrat do ReCodExu...",
"app.homepage.title": "Rozšíření SIS-CodEx",
"app.leaveGroup.confirm": "Opravdu chcete opustit tuto skupinu?",
"app.localizedTexts.externalLink": "Popis je umístěn mimo ReCodEx",
Expand Down Expand Up @@ -96,22 +99,9 @@
"app.roles.supervisorStudents": "Vedoucí-studenti",
"app.roles.supervisors": "Vedoucí",
"app.roles.supervisorsEmpowered": "Zplnomocnění vedoucí",
"app.sidebar.menu.admin.failures": "Neúspěšná odevzdání",
"app.sidebar.menu.admin.instances": "Instance",
"app.sidebar.menu.admin.messages": "Systémové zprávy",
"app.sidebar.menu.admin.server": "Správa serveru",
"app.sidebar.menu.admin.sis": "SIS integrace",
"app.sidebar.menu.admin.users": "Uživatelé",
"app.sidebar.menu.archive": "Archiv",
"app.sidebar.menu.createAccount": "Zaregistrovat se",
"app.sidebar.menu.dashboard": "Přehled",
"app.sidebar.menu.exercises": "Úlohy",
"app.sidebar.menu.faq": "FAQ",
"app.sidebar.menu.pipelines": "Pipeline",
"app.sidebar.menu.signIn": "Přihlásit se",
"app.sidebar.menu.title": "Menu",
"app.sidebar.menu.return": "Zpět do ReCodExu",
"app.sidebar.menu.user": "Osobní údaje",
"app.submissionStatus.accepted": "Toto řešení bylo označeno jako akceptované vedoucím skupiny.",
"app.sudebar.menu.admin.title": "Administrátor",
"app.user.title": "Uživatelův profil",
"app.userName.externalIds": "Externí identifikátory",
"app.userName.externalIdsClickInfo": "klikutím zkopírujete ID do schránky",
Expand All @@ -124,6 +114,5 @@
"generic.loading": "Načítání...",
"generic.noRecordsInTable": "V tabulce nejsou žádné záznamy.",
"generic.refresh": "Občerstvit",
"generic.search": "Vyhledat",
"generic.settings": "Nastavení"
"generic.search": "Vyhledat"
}
25 changes: 7 additions & 18 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
"app.groups.removeFromGroup": "Remove from group",
"app.header.languageSwitching.translationTitle": "Translation",
"app.headerNotification.copiedToClippboard": "Copied to clippboard.",
"app.homepage.processingToken": "Processing authentication token...",
"app.homepage.processingTokenFailed": "Authentication process failed.",
"app.homepage.returnToReCodEx": "Return to ReCodEx...",
"app.homepage.title": "SiS-CodEx Extension",
"app.leaveGroup.confirm": "Are you sure you want to leave this group?",
"app.localizedTexts.externalLink": "The description is located beyond the realms of ReCodEx",
Expand Down Expand Up @@ -96,22 +99,9 @@
"app.roles.supervisorStudents": "Supervisor-students",
"app.roles.supervisors": "Supervisors",
"app.roles.supervisorsEmpowered": "Empowered Supervisors",
"app.sidebar.menu.admin.failures": "Submission Failures",
"app.sidebar.menu.admin.instances": "Instances",
"app.sidebar.menu.admin.messages": "System Messages",
"app.sidebar.menu.admin.server": "Server Management",
"app.sidebar.menu.admin.sis": "SIS Integration",
"app.sidebar.menu.admin.users": "Users",
"app.sidebar.menu.archive": "Archive",
"app.sidebar.menu.createAccount": "Create account",
"app.sidebar.menu.dashboard": "Dashboard",
"app.sidebar.menu.exercises": "Exercises",
"app.sidebar.menu.faq": "FAQ",
"app.sidebar.menu.pipelines": "Pipelines",
"app.sidebar.menu.signIn": "Sign in",
"app.sidebar.menu.title": "Menu",
"app.sidebar.menu.return": "Back to ReCodEx",
"app.sidebar.menu.user": "Personal Data",
"app.submissionStatus.accepted": "This solution was marked by one of the supervisors as accepted.",
"app.sudebar.menu.admin.title": "Administration",
"app.user.title": "User's profile",
"app.userName.externalIds": "External identifiers",
"app.userName.externalIdsClickInfo": "click to copy the ID(s) to clipboard",
Expand All @@ -124,6 +114,5 @@
"generic.loading": "Loading...",
"generic.noRecordsInTable": "There are no records in the table.",
"generic.refresh": "Refresh",
"generic.search": "Search",
"generic.settings": "Settings"
}
"generic.search": "Search"
}
6 changes: 2 additions & 4 deletions src/locales/whitelist_cs.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
[
"app.roles.student",
"app.sidebar.menu.faq",
"app.sidebar.menu.title"
]
"app.roles.student"
]
40 changes: 36 additions & 4 deletions src/pages/Home/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ import { FormattedMessage, injectIntl } from 'react-intl';
import { connect } from 'react-redux';

import PageContent from '../../components/layout/PageContent';
import { HomeIcon, LoadingIcon } from '../../components/icons';
import { HomeIcon, LoadingIcon, ReturnIcon, WarningIcon } from '../../components/icons';
import Callout from '../../components/widgets/Callout';

import { setLang } from '../../redux/modules/app.js';
import { login } from '../../redux/modules/auth.js';

import { getReturnUrl, setReturnUrl } from '../../helpers/localStorage.js';
import { knownLocalesNames } from '../../helpers/localizedData.js';
import withLinks from '../../helpers/withLinks.js';

class Home extends Component {
state = { failed: false };

componentDidMount() {
const {
params: { token = null },
Expand All @@ -23,16 +27,28 @@ class Home extends Component {
links: { HOME_URI },
} = this.props;

// switch to the right language if necessary
this.setState({ failed: false });

const urlParams = new URLSearchParams(document.location.search);

// save return url into local storage (if any)
const returnUrl = urlParams.get('return');
if (returnUrl) {
setReturnUrl(returnUrl);
}

// switch to the right language if necessary
const urlLocale = urlParams.get('locale');
if (urlLocale && knownLocalesNames[urlLocale] && urlLocale !== locale) {
setLang(urlLocale);
}

// login with temp token
if (token) {
login(token).then(() => window.location.replace(HOME_URI));
login(token).then(
() => window.location.replace(HOME_URI),
() => this.setState({ failed: true })
);
}
}

Expand All @@ -46,7 +62,23 @@ class Home extends Component {
icon={<HomeIcon />}
title={<FormattedMessage id="app.homepage.title" defaultMessage="SiS-CodEx Extension" />}>
<div>
{token ? (
{this.state.failed ? (
<Callout variant="danger" className="my-3">
<p>
<WarningIcon gapRight className="text-danger" />
<FormattedMessage
id="app.homepage.processingTokenFailed"
defaultMessage="Authentication process failed."
/>
</p>
<p>
<a href={getReturnUrl()}>
<ReturnIcon gapRight />
<FormattedMessage id="app.homepage.returnToReCodEx" defaultMessage="Return to ReCodEx..." />
</a>
</p>
</Callout>
) : token ? (
<p>
<LoadingIcon gapRight />
<FormattedMessage id="app.homepage.processingToken" defaultMessage="Processing authentication token..." />
Expand Down
7 changes: 0 additions & 7 deletions src/redux/modules/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,9 @@ export { actionTypes };

export const fetchManyEndpoint = '/users';

export const loadUserData = actions.pushResource;
export const fetchUser = actions.fetchResource;
export const fetchUserIfNeeded = actions.fetchOneIfNeeded;

export const updateProfile = actions.updateResource;
export const updateSettings = (id, body) => actions.updateResource(id, body, `/users/${id}/settings`);
export const updateUIData = (id, uiData, overwrite = false) =>
actions.updateResource(id, { uiData, overwrite }, `/users/${id}/ui-data`);
export const deleteUser = actions.removeResource;

// we need the async dispatch here so we can return a resolved promise for an empty array
export const fetchByIds = ids => (dispatch, _) =>
ids && ids.length > 0
Expand Down
Loading

0 comments on commit 8d01a7f

Please sign in to comment.