feat(ui): Add local login setting (#817)

pull/820/head
TheCatLady 3 years ago committed by GitHub
parent b576f0734f
commit 9d0d5b86aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -71,6 +71,9 @@ components:
hideAvailable: hideAvailable:
type: boolean type: boolean
example: false example: false
localLogin:
type: boolean
example: true
defaultPermissions: defaultPermissions:
type: number type: number
example: 32 example: 32

@ -10,6 +10,7 @@ export interface PublicSettingsResponse {
movie4kEnabled: boolean; movie4kEnabled: boolean;
series4kEnabled: boolean; series4kEnabled: boolean;
hideAvailable: boolean; hideAvailable: boolean;
localLogin: boolean;
} }
export interface CacheItem { export interface CacheItem {

@ -54,6 +54,7 @@ export interface MainSettings {
csrfProtection: boolean; csrfProtection: boolean;
defaultPermissions: number; defaultPermissions: number;
hideAvailable: boolean; hideAvailable: boolean;
localLogin: boolean;
trustProxy: boolean; trustProxy: boolean;
} }
@ -65,6 +66,7 @@ interface FullPublicSettings extends PublicSettings {
movie4kEnabled: boolean; movie4kEnabled: boolean;
series4kEnabled: boolean; series4kEnabled: boolean;
hideAvailable: boolean; hideAvailable: boolean;
localLogin: boolean;
} }
export interface NotificationAgentConfig { export interface NotificationAgentConfig {
@ -162,6 +164,7 @@ class Settings {
csrfProtection: false, csrfProtection: false,
defaultPermissions: Permission.REQUEST, defaultPermissions: Permission.REQUEST,
hideAvailable: false, hideAvailable: false,
localLogin: true,
trustProxy: false, trustProxy: false,
}, },
plex: { plex: {
@ -296,6 +299,7 @@ class Settings {
(sonarr) => sonarr.is4k && sonarr.isDefault (sonarr) => sonarr.is4k && sonarr.isDefault
), ),
hideAvailable: this.data.main.hideAvailable, hideAvailable: this.data.main.hideAvailable,
localLogin: this.data.main.localLogin,
}; };
} }

@ -134,10 +134,13 @@ authRoutes.post('/login', async (req, res, next) => {
}); });
authRoutes.post('/local', async (req, res, next) => { authRoutes.post('/local', async (req, res, next) => {
const settings = getSettings();
const userRepository = getRepository(User); const userRepository = getRepository(User);
const body = req.body as { email?: string; password?: string }; const body = req.body as { email?: string; password?: string };
if (!body.email || !body.password) { if (!settings.main.localLogin) {
return res.status(500).json({ error: 'Local user login is disabled' });
} else if (!body.email || !body.password) {
return res return res
.status(500) .status(500)
.json({ error: 'You must provide an email and a password' }); .json({ error: 'You must provide an email and a password' });

@ -9,6 +9,7 @@ import Transition from '../Transition';
import LanguagePicker from '../Layout/LanguagePicker'; import LanguagePicker from '../Layout/LanguagePicker';
import LocalLogin from './LocalLogin'; import LocalLogin from './LocalLogin';
import Accordion from '../Common/Accordion'; import Accordion from '../Common/Accordion';
import useSettings from '../../hooks/useSettings';
const messages = defineMessages({ const messages = defineMessages({
signinheader: 'Sign in to continue', signinheader: 'Sign in to continue',
@ -23,6 +24,7 @@ const Login: React.FC = () => {
const [authToken, setAuthToken] = useState<string | undefined>(undefined); const [authToken, setAuthToken] = useState<string | undefined>(undefined);
const { user, revalidate } = useUser(); const { user, revalidate } = useUser();
const router = useRouter(); const router = useRouter();
const settings = useSettings();
// Effect that is triggered when the `authToken` comes back from the Plex OAuth // Effect that is triggered when the `authToken` comes back from the Plex OAuth
// We take the token and attempt to login. If we get a success message, we will // We take the token and attempt to login. If we get a success message, we will
@ -124,10 +126,14 @@ const Login: React.FC = () => {
{({ openIndexes, handleClick, AccordionContent }) => ( {({ openIndexes, handleClick, AccordionContent }) => (
<> <>
<button <button
className={`text-sm w-full focus:outline-none transition-colors duration-200 py-2 bg-gray-800 hover:bg-gray-700 bg-opacity-70 hover:bg-opacity-70 sm:rounded-t-lg text-center text-gray-400 ${ className={`w-full py-2 text-sm text-center text-gray-400 transition-colors duration-200 bg-gray-800 cursor-default focus:outline-none bg-opacity-70 sm:rounded-t-lg ${
openIndexes.includes(0) && 'text-indigo-500' openIndexes.includes(0) && 'text-indigo-500'
} ${
settings.currentSettings.localLogin &&
'hover:bg-gray-700 hover:cursor-pointer'
}`} }`}
onClick={() => handleClick(0)} onClick={() => handleClick(0)}
disabled={!settings.currentSettings.localLogin}
> >
{intl.formatMessage(messages.signinwithplex)} {intl.formatMessage(messages.signinwithplex)}
</button> </button>
@ -139,21 +145,25 @@ const Login: React.FC = () => {
/> />
</div> </div>
</AccordionContent> </AccordionContent>
<button {settings.currentSettings.localLogin && (
className={`text-sm w-full focus:outline-none transition-colors duration-200 py-2 bg-gray-800 hover:bg-gray-700 bg-opacity-70 hover:bg-opacity-70 text-center text-gray-400 ${ <div>
openIndexes.includes(1) <button
? 'text-indigo-500' className={`w-full py-2 text-sm text-center text-gray-400 transition-colors duration-200 bg-gray-800 cursor-default focus:outline-none bg-opacity-70 sm:rounded-t-lg hover:bg-gray-700 hover:cursor-pointer ${
: 'sm:rounded-b-lg ' openIndexes.includes(1)
}`} ? 'text-indigo-500'
onClick={() => handleClick(1)} : 'sm:rounded-b-lg '
> }`}
{intl.formatMessage(messages.signinwithoverseerr)} onClick={() => handleClick(1)}
</button> >
<AccordionContent isOpen={openIndexes.includes(1)}> {intl.formatMessage(messages.signinwithoverseerr)}
<div className="px-10 py-8"> </button>
<LocalLogin revalidate={revalidate} /> <AccordionContent isOpen={openIndexes.includes(1)}>
<div className="px-10 py-8">
<LocalLogin revalidate={revalidate} />
</div>
</AccordionContent>
</div> </div>
</AccordionContent> )}
</> </>
)} )}
</Accordion> </Accordion>

@ -35,6 +35,9 @@ const messages = defineMessages({
trustProxy: 'Enable Proxy Support', trustProxy: 'Enable Proxy Support',
trustProxyTip: trustProxyTip:
'Allows Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be reloaded for changes to take effect)', 'Allows Overseerr to correctly register client IP addresses behind a proxy (Overseerr must be reloaded for changes to take effect)',
localLogin: 'Enable Local User Sign-In',
localLoginTip:
'Disabling this option only prevents new sign-ins (no user data is deleted)',
}); });
const SettingsMain: React.FC = () => { const SettingsMain: React.FC = () => {
@ -83,6 +86,7 @@ const SettingsMain: React.FC = () => {
csrfProtection: data?.csrfProtection, csrfProtection: data?.csrfProtection,
defaultPermissions: data?.defaultPermissions ?? 0, defaultPermissions: data?.defaultPermissions ?? 0,
hideAvailable: data?.hideAvailable, hideAvailable: data?.hideAvailable,
localLogin: data?.localLogin,
trustProxy: data?.trustProxy, trustProxy: data?.trustProxy,
}} }}
enableReinitialize enableReinitialize
@ -93,6 +97,7 @@ const SettingsMain: React.FC = () => {
csrfProtection: values.csrfProtection, csrfProtection: values.csrfProtection,
defaultPermissions: values.defaultPermissions, defaultPermissions: values.defaultPermissions,
hideAvailable: values.hideAvailable, hideAvailable: values.hideAvailable,
localLogin: values.localLogin,
trustProxy: values.trustProxy, trustProxy: values.trustProxy,
}); });
@ -172,9 +177,7 @@ const SettingsMain: React.FC = () => {
</div> </div>
<div className="form-row"> <div className="form-row">
<label htmlFor="trustProxy" className="checkbox-label"> <label htmlFor="trustProxy" className="checkbox-label">
<span className="mr-2"> <span>{intl.formatMessage(messages.trustProxy)}</span>
{intl.formatMessage(messages.trustProxy)}
</span>
<span className="label-tip"> <span className="label-tip">
{intl.formatMessage(messages.trustProxyTip)} {intl.formatMessage(messages.trustProxyTip)}
</span> </span>
@ -236,6 +239,24 @@ const SettingsMain: React.FC = () => {
/> />
</div> </div>
</div> </div>
<div className="form-row">
<label htmlFor="localLogin" className="checkbox-label">
<span>{intl.formatMessage(messages.localLogin)}</span>
<span className="label-tip">
{intl.formatMessage(messages.localLoginTip)}
</span>
</label>
<div className="form-input">
<Field
type="checkbox"
id="localLogin"
name="localLogin"
onChange={() => {
setFieldValue('localLogin', !values.localLogin);
}}
/>
</div>
</div>
<div <div
role="group" role="group"
aria-labelledby="group-label" aria-labelledby="group-label"

@ -205,7 +205,7 @@ const SettingsNotifications: React.FC = ({ children }) => {
<Form className="section"> <Form className="section">
<div className="form-row"> <div className="form-row">
<label htmlFor="name" className="checkbox-label"> <label htmlFor="name" className="checkbox-label">
<span className="mr-2"> <span>
{intl.formatMessage(messages.enablenotifications)} {intl.formatMessage(messages.enablenotifications)}
</span> </span>
</label> </label>
@ -222,7 +222,7 @@ const SettingsNotifications: React.FC = ({ children }) => {
</div> </div>
<div className="form-row"> <div className="form-row">
<label htmlFor="name" className="checkbox-label"> <label htmlFor="name" className="checkbox-label">
<span className="mr-2"> <span>
{intl.formatMessage(messages.autoapprovedrequests)} {intl.formatMessage(messages.autoapprovedrequests)}
</span> </span>
</label> </label>

@ -351,7 +351,7 @@ const SettingsPlex: React.FC<SettingsPlexProps> = ({ onComplete }) => {
<div className="form-row"> <div className="form-row">
<label htmlFor="name" className="text-label"> <label htmlFor="name" className="text-label">
<div className="flex flex-col"> <div className="flex flex-col">
<span className="mr-2"> <span>
<FormattedMessage {...messages.servername} /> <FormattedMessage {...messages.servername} />
</span> </span>
<span className="text-gray-500"> <span className="text-gray-500">

@ -11,6 +11,7 @@ const defaultSettings = {
movie4kEnabled: false, movie4kEnabled: false,
series4kEnabled: false, series4kEnabled: false,
hideAvailable: false, hideAvailable: false,
localLogin: false,
}; };
export const SettingsContext = React.createContext<SettingsContextProps>({ export const SettingsContext = React.createContext<SettingsContextProps>({

@ -433,6 +433,8 @@
"components.Settings.hideAvailable": "Hide Available Media", "components.Settings.hideAvailable": "Hide Available Media",
"components.Settings.hostname": "Hostname/IP", "components.Settings.hostname": "Hostname/IP",
"components.Settings.librariesRemaining": "Libraries Remaining: {count}", "components.Settings.librariesRemaining": "Libraries Remaining: {count}",
"components.Settings.localLogin": "Enable Local User Sign-In",
"components.Settings.localLoginTip": "Disabling this option only prevents new sign-ins (no user data is deleted)",
"components.Settings.manualscan": "Manual Library Scan", "components.Settings.manualscan": "Manual Library Scan",
"components.Settings.manualscanDescription": "Normally, this will only be run once every 24 hours. Overseerr will check your Plex server's recently added more aggressively. If this is your first time configuring Plex, a one-time full manual library scan is recommended!", "components.Settings.manualscanDescription": "Normally, this will only be run once every 24 hours. Overseerr will check your Plex server's recently added more aggressively. If this is your first time configuring Plex, a one-time full manual library scan is recommended!",
"components.Settings.menuAbout": "About", "components.Settings.menuAbout": "About",

@ -142,6 +142,7 @@ CoreApp.getInitialProps = async (initialProps) => {
hideAvailable: false, hideAvailable: false,
movie4kEnabled: false, movie4kEnabled: false,
series4kEnabled: false, series4kEnabled: false,
localLogin: true,
}; };
let locale = 'en'; let locale = 'en';

Loading…
Cancel
Save