Skip to content

Commit

Permalink
Ensure self-hosted instance can use SendGrid (#318)
Browse files Browse the repository at this point in the history
  • Loading branch information
antoinejaussoin authored Oct 14, 2021
1 parent 0c11b7e commit 94a5a6d
Show file tree
Hide file tree
Showing 16 changed files with 64 additions and 102 deletions.
4 changes: 2 additions & 2 deletions backend/src/admin/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import config from '../config';
import { isLicenced } from '../security/is-licenced';
import {
AdminChangePasswordPayload,
SelfHostingPayload,
BackendCapabilities,
} from '@retrospected/common';
import { getIdentityFromRequest, hashPassword } from '../utils';
import csurf from 'csurf';
Expand All @@ -18,7 +18,7 @@ const csrfProtection = csurf();

router.get('/self-hosting', async (_, res) => {
const licence = await isLicenced();
const payload: SelfHostingPayload = {
const payload: BackendCapabilities = {
adminEmail: config.SELF_HOSTED_ADMIN,
selfHosted: config.SELF_HOSTED,
licenced: !!licence,
Expand Down
14 changes: 10 additions & 4 deletions backend/src/security/is-licenced.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,16 @@ async function isLicencedBase(): Promise<LicenceMetadata | null> {
const result = (await response.json()) as LicenceMetadata;
return result;
} else {
console.error(
'Could not contact the licence server. If you have a valid licence, please contact [email protected] for support.'
);
console.log(response.status, response.statusText);
if (response.status === 403) {
console.error(
'The licence key is not recognised. If you have a valid licence, please contact [email protected] for support.'
);
} else {
console.error(
'Could not contact the licence server. If you have a valid licence, please contact [email protected] for support.'
);
console.log(response.status, response.statusText);
}
}
} catch (err) {
console.error(
Expand Down
2 changes: 1 addition & 1 deletion common/src/payloads.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ export interface SelfHostedCheckPayload {
key: string;
}

export interface SelfHostingPayload {
export interface BackendCapabilities {
selfHosted: boolean;
sendGridAvailable: boolean;
adminEmail: string;
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import { HomeOutlined } from '@mui/icons-material';
import ProPill from './components/ProPill';
import { CodeSplitLoader } from './CodeSplitLoader';
import useSidePanel from './views/panel/useSidePanel';
import useIsLicenced from './global/useIsLicenced';
import { Alert, AlertTitle } from '@mui/material';
import useBackendCapabilities from './global/useBackendCapabilities';

const Home = lazy(() => import('./views/Home' /* webpackChunkName: "home" */));
const Game = lazy(() => import('./views/Game' /* webpackChunkName: "game" */));
Expand Down Expand Up @@ -85,7 +85,7 @@ const Title = styled(Typography)`

function App() {
const history = useHistory();
const licenced = useIsLicenced();
const backend = useBackendCapabilities();
const isCompatible = useIsCompatibleBrowser();
const { toggle: togglePanel } = useSidePanel();
const isInitialised = useIsInitialised();
Expand All @@ -102,7 +102,7 @@ function App() {
}, [history]);
return (
<div>
{!licenced ? (
{!backend.licenced ? (
<Alert title="Unlicenced" severity="error">
<AlertTitle>Retrospected is Unlicenced</AlertTitle>
This software is unlicenced. You can obtain a licence{' '}
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
ResetChangePasswordPayload,
FullUser,
Product,
SelfHostingPayload,
BackendCapabilities,
} from '@retrospected/common';
import config from '../utils/getConfig';
import { v4 } from 'uuid';
Expand Down Expand Up @@ -223,8 +223,8 @@ export async function getGiphyUrl(giphyId: string): Promise<string | null> {
}
}

export async function fetchSelfHostingInfo(): Promise<SelfHostingPayload | null> {
return await fetchGet<SelfHostingPayload | null>(
export async function fetchBackendCapabilities(): Promise<BackendCapabilities | null> {
return await fetchGet<BackendCapabilities | null>(
'/api/admin/self-hosting',
null
);
Expand Down
14 changes: 6 additions & 8 deletions frontend/src/auth/modal/account/LostPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@ import Input from '../../../components/Input';
import { Email } from '@mui/icons-material';
import { resetPassword } from '../../../api';
import { Link } from 'react-router-dom';
import useAdminEmail from '../../../global/useAdminEmail';
import useIsSendGridAvailable from '../../../global/useIsSendGridAvailable';
import useBackendCapabilities from '../../../global/useBackendCapabilities';

const LostPassword = () => {
const { ResetPassword: translations, AuthCommon: authTranslations } =
useTranslations();
const [email, setEmail] = useState('');
const [done, setDone] = useState(false);
const adminEmail = useAdminEmail();
const canUseSendgrid = useIsSendGridAvailable();
const backend = useBackendCapabilities();
const handleForgotPassword = useCallback(() => {
async function reset() {
await resetPassword(email);
Expand All @@ -25,12 +23,12 @@ const LostPassword = () => {
reset();
}, [email]);

if (!canUseSendgrid) {
if (!backend.sendGridAvailable) {
return (
<Alert severity="info">
You are using a Self-Hosted version of Retrospected. In order to reset
your password, ask your admin ({adminEmail}) to access the admin page to
do that:&nbsp;
You are using a Self-Hosted version of Retrospected, without email
support. In order to reset your password, ask your admin (
{backend.adminEmail}) to access the admin page to do that:&nbsp;
<Link to="/admin">Admin Panel</Link>
</Alert>
);
Expand Down
8 changes: 3 additions & 5 deletions frontend/src/auth/useIsPro.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import useIsLicenced from '../global/useIsLicenced';
import useIsSelfHosted from '../global/useIsSelfHosted';
import useBackendCapabilities from '../global/useBackendCapabilities';
import useUser from './useUser';

export default function useIsPro() {
const user = useUser();
const isSelfHosted = useIsSelfHosted();
const isLicenced = useIsLicenced();
if (isSelfHosted && isLicenced) {
const backend = useBackendCapabilities();
if (backend.selfHosted && backend.licenced) {
return true;
}
const activeTrial = user && user.trial && new Date(user.trial) > new Date();
Expand Down
23 changes: 6 additions & 17 deletions frontend/src/global/GlobalProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,22 @@
import { useEffect } from 'react';
import { fetchSelfHostingInfo } from '../api';
import { fetchBackendCapabilities } from '../api';
import { useSetRecoilState } from 'recoil';
import {
adminEmailState,
isLicencedState,
oauthAvailabilitiesState,
selfHostedState,
} from './state';
import { backendCapabilitiesState } from './state';
import { loadCsrfToken } from '../api/fetch';

const GlobalProvider: React.FC = ({ children }) => {
const setEmail = useSetRecoilState(adminEmailState);
const setLicenced = useSetRecoilState(isLicencedState);
const setSelfHosted = useSetRecoilState(selfHostedState);
const setOAuth = useSetRecoilState(oauthAvailabilitiesState);
const setBackendCapabilities = useSetRecoilState(backendCapabilitiesState);

useEffect(() => {
async function loadGlobal() {
await loadCsrfToken(); // Make sure the CSRF token is loaded before anything else
const infos = await fetchSelfHostingInfo();
const infos = await fetchBackendCapabilities();
if (infos) {
setEmail(infos.adminEmail);
setLicenced(infos.licenced);
setSelfHosted(infos.selfHosted);
setOAuth(infos.oAuth);
setBackendCapabilities(infos);
}
}
loadGlobal();
}, [setEmail, setLicenced, setSelfHosted, setOAuth]);
}, [setBackendCapabilities]);

return <>{children}</>;
};
Expand Down
40 changes: 15 additions & 25 deletions frontend/src/global/state.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,20 @@
import { atom } from 'recoil';
import { OAuthAvailabilities } from '@retrospected/common';
import { BackendCapabilities } from '@retrospected/common';

export const adminEmailState = atom<string | null>({
key: 'ADMIN_EMAIL',
default: null,
});
export const isLicencedState = atom<boolean>({
key: 'LICENCED',
default: true,
});
export const selfHostedState = atom<boolean>({
key: 'SELF-HOSTED',
default: false,
});
export const oauthAvailabilitiesState = atom<OAuthAvailabilities>({
key: 'OAuthAvailabilities',
export const backendCapabilitiesState = atom<BackendCapabilities>({
key: 'BACKEND_CAPABILITIES',
default: {
google: false,
github: false,
twitter: false,
microsoft: false,
slack: false,
okta: false,
adminEmail: '',
licenced: true,
selfHosted: false,
oAuth: {
google: false,
github: false,
twitter: false,
microsoft: false,
slack: false,
okta: false,
},
sendGridAvailable: false,
},
});
export const isSendGridAvailableState = atom<boolean>({
key: 'SENDGRID_AVAILABLE',
default: false,
});
6 changes: 0 additions & 6 deletions frontend/src/global/useAdminEmail.ts

This file was deleted.

7 changes: 7 additions & 0 deletions frontend/src/global/useBackendCapabilities.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { BackendCapabilities } from '@retrospected/common';
import { useRecoilValue } from 'recoil';
import { backendCapabilitiesState } from './state';

export default function useBackendCapabilities(): BackendCapabilities {
return useRecoilValue(backendCapabilitiesState);
}
6 changes: 0 additions & 6 deletions frontend/src/global/useIsLicenced.ts

This file was deleted.

6 changes: 0 additions & 6 deletions frontend/src/global/useIsSelfHosted.ts

This file was deleted.

6 changes: 0 additions & 6 deletions frontend/src/global/useIsSendGridAvailable.ts

This file was deleted.

6 changes: 3 additions & 3 deletions frontend/src/global/useOAuthAvailabilities.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { some, values } from 'lodash';
import { useRecoilValue } from 'recoil';
import { oauthAvailabilitiesState } from './state';
import useBackendCapabilities from './useBackendCapabilities';

export default function useOAuthAvailabilities() {
const details = useRecoilValue(oauthAvailabilitiesState);
const backend = useBackendCapabilities();
const details = backend.oAuth;
return {
any: some(values(details)),
details,
Expand Down
12 changes: 5 additions & 7 deletions frontend/src/views/admin/AdminPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@ import { Alert } from '@mui/material';
import { DataGrid, GridColDef } from '@mui/x-data-grid';
import { FullUser } from '@retrospected/common';
import useUser from '../../auth/useUser';
import useAdminEmail from '../../global/useAdminEmail';
import useStateFetch from '../../hooks/useStateFetch';
import { useMemo } from 'react';
import styled from '@emotion/styled';
import ChangePassword from './ChangePassword';
import useIsSelfHosted from '../../global/useIsSelfHosted';
import useBackendCapabilities from '../../global/useBackendCapabilities';

export default function AdminPage() {
const user = useUser();
const adminEmail = useAdminEmail();
const isSelfHosted = useIsSelfHosted();
const backend = useBackendCapabilities();
const [users] = useStateFetch<FullUser[]>('/api/admin/users', []);

const columns: GridColDef[] = useMemo(() => {
Expand All @@ -28,16 +26,16 @@ export default function AdminPage() {
] as GridColDef[];
}, []);

if (!isSelfHosted) {
if (!backend.selfHosted) {
<Alert severity="error">
This page is only accessible for self-hosted instances.
</Alert>;
}
if (!user || user.email !== adminEmail) {
if (!user || user.email !== backend.adminEmail) {
return (
<Alert severity="error">
This page is only accessible for the Self-Hosted Administrator (
{adminEmail}).
{backend.adminEmail}).
</Alert>
);
}
Expand Down

0 comments on commit 94a5a6d

Please sign in to comment.