Skip to content

Commit

Permalink
feat: support disabling jellyfin login
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelhthomas committed Jan 19, 2025
1 parent ca73931 commit 4d7eba8
Show file tree
Hide file tree
Showing 34 changed files with 185 additions and 51 deletions.
8 changes: 8 additions & 0 deletions docs/using-jellyseerr/settings/users.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ When disabled, your mediaserver OAuth becomes the only sign-in option, and any "

This setting is **enabled** by default.

## Enable Jellyfin/Emby/Plex Sign-In

When enabled, users will be able to sign in to Jellyseerr using their Jellyfin/Emby/Plex credentials, provided they have linked their media server accounts.

When disabled, users will only be able to sign in using their email address. Users without a password set will not be able to sign in to Jellyseerr.

This setting is **enabled** by default.

## Enable New Jellyfin/Emby/Plex Sign-In

When enabled, users with access to your media server will be able to sign in to Jellyseerr even if they have not yet been imported. Users will be automatically assigned the permissions configured in the [Default Permissions](#default-permissions) setting upon first sign-in.
Expand Down
1 change: 1 addition & 0 deletions server/interfaces/api/settingsInterfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export interface PublicSettingsResponse {
applicationUrl: string;
hideAvailable: boolean;
localLogin: boolean;
mediaServerLogin: boolean;
movie4kEnabled: boolean;
series4kEnabled: boolean;
discoverRegion: string;
Expand Down
4 changes: 4 additions & 0 deletions server/lib/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export interface MainSettings {
};
hideAvailable: boolean;
localLogin: boolean;
mediaServerLogin: boolean;
newPlexLogin: boolean;
discoverRegion: string;
streamingRegion: string;
Expand All @@ -147,6 +148,7 @@ interface FullPublicSettings extends PublicSettings {
applicationUrl: string;
hideAvailable: boolean;
localLogin: boolean;
mediaServerLogin: boolean;
movie4kEnabled: boolean;
series4kEnabled: boolean;
discoverRegion: string;
Expand Down Expand Up @@ -340,6 +342,7 @@ class Settings {
},
hideAvailable: false,
localLogin: true,
mediaServerLogin: true,
newPlexLogin: true,
discoverRegion: '',
streamingRegion: '',
Expand Down Expand Up @@ -582,6 +585,7 @@ class Settings {
applicationUrl: this.data.main.applicationUrl,
hideAvailable: this.data.main.hideAvailable,
localLogin: this.data.main.localLogin,
mediaServerLogin: this.data.main.mediaServerLogin,
jellyfinForgotPasswordUrl: this.data.jellyfin.jellyfinForgotPasswordUrl,
movie4kEnabled: this.data.radarr.some(
(radarr) => radarr.is4k && radarr.isDefault
Expand Down
14 changes: 9 additions & 5 deletions server/routes/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ authRoutes.post('/plex', async (req, res, next) => {
}

if (
settings.main.mediaServerType != MediaServerType.PLEX &&
settings.main.mediaServerType != MediaServerType.NOT_CONFIGURED
settings.main.mediaServerType != MediaServerType.NOT_CONFIGURED &&
(settings.main.mediaServerLogin === false ||
settings.main.mediaServerType != MediaServerType.PLEX)
) {
return res.status(500).json({ error: 'Plex login is disabled' });
}
Expand Down Expand Up @@ -231,10 +232,13 @@ authRoutes.post('/jellyfin', async (req, res, next) => {

//Make sure jellyfin login is enabled, but only if jellyfin && Emby is not already configured
if (
settings.main.mediaServerType !== MediaServerType.JELLYFIN &&
settings.main.mediaServerType !== MediaServerType.EMBY &&
// media server not configured, allow login for setup
settings.main.mediaServerType != MediaServerType.NOT_CONFIGURED &&
settings.jellyfin.ip !== ''
(settings.main.mediaServerLogin === false ||
// media server is neither jellyfin or emby
(settings.main.mediaServerType !== MediaServerType.JELLYFIN &&
settings.main.mediaServerType !== MediaServerType.EMBY &&
settings.jellyfin.ip !== ''))
) {
return res.status(500).json({ error: 'Jellyfin login is disabled' });
}
Expand Down
43 changes: 43 additions & 0 deletions src/components/Common/LabeledCheckbox/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { Field } from 'formik';

interface LabeledCheckboxProps {
id: string;
className?: string;
label: string;
description: string;
onChange: () => void;
children?: React.ReactNode;
}

const LabeledCheckbox: React.FC<LabeledCheckboxProps> = ({
id,
className,
label,
description,
onChange,
children,
}) => {
return (
<>
<div className={`relative flex items-start ${className}`}>
<div className="flex h-6 items-center">
<Field type="checkbox" id={id} name={id} onChange={onChange} />
</div>
<div className="ml-3 text-sm leading-6">
<label htmlFor="localLogin" className="block">
<div className="flex flex-col">
<span className="font-medium text-white">{label}</span>
<span className="font-normal text-gray-400">{description}</span>
</div>
</label>
</div>
</div>
{
/* can hold child checkboxes */
children && <div className="mt-4 pl-10">{children}</div>
}
</>
);
};

export default LabeledCheckbox;
107 changes: 87 additions & 20 deletions src/components/Settings/SettingsUsers/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Button from '@app/components/Common/Button';
import LabeledCheckbox from '@app/components/Common/LabeledCheckbox';
import LoadingSpinner from '@app/components/Common/LoadingSpinner';
import PageTitle from '@app/components/Common/PageTitle';
import PermissionEdit from '@app/components/PermissionEdit';
Expand All @@ -10,19 +11,26 @@ import { ArrowDownOnSquareIcon } from '@heroicons/react/24/outline';
import { MediaServerType } from '@server/constants/server';
import type { MainSettings } from '@server/lib/settings';
import { Field, Form, Formik } from 'formik';
import { useIntl } from 'react-intl';
import { useIntl, type IntlShape } from 'react-intl';
import { useToasts } from 'react-toast-notifications';
import useSWR, { mutate } from 'swr';
import * as yup from 'yup';

const messages = defineMessages('components.Settings.SettingsUsers', {
users: 'Users',
userSettings: 'User Settings',
userSettingsDescription: 'Configure global and default user settings.',
toastSettingsSuccess: 'User settings saved successfully!',
toastSettingsFailure: 'Something went wrong while saving settings.',
loginMethods: 'Login Methods',
loginMethodsTip: 'Configure login methods for users.',
localLogin: 'Enable Local Sign-In',
localLoginTip:
'Allow users to sign in using their email address and password, instead of {mediaServerName} OAuth',
'Allow users to sign in using their email address and password',
mediaServerLogin: 'Enable {mediaServerName} Sign-In',
mediaServerLoginTip:
'Allow users to sign in using their {mediaServerName} account',
atLeastOneAuth: 'At least one authentication method must be selected.',
newPlexLogin: 'Enable New {mediaServerName} Sign-In',
newPlexLoginTip:
'Allow {mediaServerName} users to sign in without first being imported',
Expand All @@ -32,6 +40,29 @@ const messages = defineMessages('components.Settings.SettingsUsers', {
defaultPermissionsTip: 'Initial permissions assigned to new users',
});

const createValidationSchema = (intl: IntlShape) => {
return yup
.object()
.shape({
localLogin: yup.boolean(),
mediaServerLogin: yup.boolean(),
})
.test({
name: 'atLeastOneAuth',
test: function (values) {
const isValid = ['localLogin', 'mediaServerLogin'].some(
(field) => !!values[field]
);

if (isValid) return true;
return this.createError({
path: 'localLogin | mediaServerLogin',
message: intl.formatMessage(messages.atLeastOneAuth),
});
},
});
};

const SettingsUsers = () => {
const { addToast } = useToasts();
const intl = useIntl();
Expand All @@ -52,6 +83,8 @@ const SettingsUsers = () => {
? 'Jellyfin'
: settings.currentSettings.mediaServerType === MediaServerType.EMBY
? 'Emby'
: settings.currentSettings.mediaServerType === MediaServerType.PLEX
? 'Plex'
: undefined,
};

Expand All @@ -73,13 +106,15 @@ const SettingsUsers = () => {
<Formik
initialValues={{
localLogin: data?.localLogin,
mediaServerLogin: data?.mediaServerLogin,
newPlexLogin: data?.newPlexLogin,
movieQuotaLimit: data?.defaultQuotas.movie.quotaLimit ?? 0,
movieQuotaDays: data?.defaultQuotas.movie.quotaDays ?? 7,
tvQuotaLimit: data?.defaultQuotas.tv.quotaLimit ?? 0,
tvQuotaDays: data?.defaultQuotas.tv.quotaDays ?? 7,
defaultPermissions: data?.defaultPermissions ?? 0,
}}
validationSchema={() => createValidationSchema(intl)}
enableReinitialize
onSubmit={async (values) => {
try {
Expand All @@ -90,6 +125,7 @@ const SettingsUsers = () => {
},
body: JSON.stringify({
localLogin: values.localLogin,
mediaServerLogin: values.mediaServerLogin,
newPlexLogin: values.newPlexLogin,
defaultQuotas: {
movie: {
Expand Down Expand Up @@ -121,30 +157,61 @@ const SettingsUsers = () => {
}
}}
>
{({ isSubmitting, values, setFieldValue }) => {
{({ isSubmitting, values, errors, setFieldValue }) => {
return (
<Form className="section">
<div className="form-row">
<label htmlFor="localLogin" className="checkbox-label">
{intl.formatMessage(messages.localLogin)}
<span className="label-tip">
{intl.formatMessage(
messages.localLoginTip,
mediaServerFormatValues
<div
role="group"
aria-labelledby="group-label"
className="form-group"
>
<div className="form-row">
<span id="group-label" className="group-label">
{intl.formatMessage(messages.loginMethods)}
<span className="label-tip">
{intl.formatMessage(messages.loginMethodsTip)}
</span>
{'localLogin | mediaServerLogin' in errors && (
<span className="error">
{errors['localLogin | mediaServerLogin'] as string}
</span>
)}
</span>
</label>
<div className="form-input-area">
<Field
type="checkbox"
id="localLogin"
name="localLogin"
onChange={() => {
setFieldValue('localLogin', !values.localLogin);
}}
/>

<div className="form-input-area max-w-lg">
<LabeledCheckbox
id="localLogin"
label={intl.formatMessage(messages.localLogin)}
description={intl.formatMessage(
messages.localLoginTip,
mediaServerFormatValues
)}
onChange={() =>
setFieldValue('localLogin', !values.localLogin)
}
/>
<LabeledCheckbox
id="mediaServerLogin"
className="mt-4"
label={intl.formatMessage(
messages.mediaServerLogin,
mediaServerFormatValues
)}
description={intl.formatMessage(
messages.mediaServerLoginTip,
mediaServerFormatValues
)}
onChange={() =>
setFieldValue(
'mediaServerLogin',
!values.mediaServerLogin
)
}
/>
</div>
</div>
</div>

<div className="form-row">
<label htmlFor="newPlexLogin" className="checkbox-label">
{intl.formatMessage(
Expand Down
1 change: 1 addition & 0 deletions src/context/SettingsContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const defaultSettings = {
applicationUrl: '',
hideAvailable: false,
localLogin: true,
mediaServerLogin: true,
movie4kEnabled: false,
series4kEnabled: false,
discoverRegion: '',
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/locale/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -630,7 +630,7 @@
"components.Settings.SettingsUsers.defaultPermissions": "الاذونات الافتراضية",
"components.Settings.SettingsUsers.defaultPermissionsTip": "الإذونات الأساسية للمستخدمين الجدد",
"components.Settings.SettingsUsers.localLogin": "تفعيل تسجيل الدخول محليا",
"components.Settings.SettingsUsers.localLoginTip": "السماح للمستخدمين بتسجيل الدخول بإستخدام البريد الإلكتروني وكلمة السر بدل التصادق مع بليكس",
"components.Settings.SettingsUsers.localLoginTip": "السماح للمستخدمين بتسجيل الدخول بإستخدام البريد الإلكتروني وكلمة السر",
"components.Settings.SettingsUsers.movieRequestLimitLabel": "الحد الأعلى لطلبات الأفلام ( تعيين عام )",
"components.Settings.SettingsUsers.newPlexLogin": "تفعيل تسجيل الدخول لمستخدمين بليكس الجدد",
"components.Settings.SettingsUsers.toastSettingsFailure": "حدث خطأ ما أثناء حفظ الإعدادات.",
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/locale/bg.json
Original file line number Diff line number Diff line change
Expand Up @@ -1141,7 +1141,7 @@
"components.Setup.setup": "Настройване",
"components.UserProfile.emptywatchlist": "Мултимедията, добавена към вашия <PlexWatchlistSupportLink>списък за гледане в Plex</PlexWatchlistSupportLink>, ще се появи тук.",
"components.Settings.enablessl": "Използвай SSL",
"components.Settings.SettingsUsers.localLoginTip": "Позволете на потребителите да влизат, като използват своя имейл адрес и парола, вместо Plex OAuth",
"components.Settings.SettingsUsers.localLoginTip": "Позволете на потребителите да влизат, като използват своя имейл адрес и парола",
"components.Settings.noDefaultNon4kServer": "Ако имате само един сървър {serverType} както за съдържание, което не е 4K, така и за 4K (или ако изтегляте само 4K съдържание), вашият сървър {serverType} трябва <strong>ДА НЕ БЪДЕ</strong> обозначен като 4K сървър.",
"components.UserList.nouserstoimport": "Няма Plex потребители за импортиране.",
"components.UserProfile.ProfileHeader.profile": "Виж профил",
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/locale/ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -782,7 +782,7 @@
"components.Settings.Notifications.webhookUrlTip": "Creeu una <DiscordWebhookLink>integració de webhook</DiscordWebhookLink> al vostre servidor",
"components.Settings.Notifications.encryptionOpportunisticTls": "Utilitzeu sempre STARTTLS",
"components.Settings.Notifications.encryptionNone": "Cap",
"components.Settings.SettingsUsers.localLoginTip": "Permetre als usuaris iniciar la sessió mitjançant la seva adreça de correu electrònic i contrasenya, en lloc de l'autenticació de Plex",
"components.Settings.SettingsUsers.localLoginTip": "Permetre als usuaris iniciar la sessió mitjançant la seva adreça de correu electrònic i contrasenya",
"components.Settings.SettingsUsers.defaultPermissionsTip": "Permisos inicials assignats a usuaris nous",
"components.RequestList.RequestItem.requesteddate": "Sol·licitat",
"components.RequestCard.failedretry": "S'ha produït un error en tornar a demanar la sol·licitud.",
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/locale/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -913,7 +913,7 @@
"components.Settings.Notifications.telegramsettingsfailed": "Nastavení oznámení Telegramu se nepodařilo uložit.",
"components.Settings.RadarrModal.toastRadarrTestFailure": "Nepodařilo se připojit k Radarru.",
"components.Settings.SettingsUsers.localLogin": "Povolení místního přihlášení",
"components.Settings.SettingsUsers.localLoginTip": "Umožnit uživatelům přihlašovat se pomocí e-mailové adresy a hesla namísto protokolu Plex OAuth",
"components.Settings.SettingsUsers.localLoginTip": "Umožnit uživatelům přihlašovat se pomocí e-mailové adresy a hesla",
"components.UserProfile.UserSettings.UserNotificationSettings.discordsettingsfailed": "Nastavení oznámení Discordu se nepodařilo uložit.",
"components.UserProfile.UserSettings.UserNotificationSettings.pushbulletsettingssaved": "Nastavení oznámení Pushbullet úspěšně uloženo!",
"components.UserProfile.UserSettings.UserNotificationSettings.validationPushbulletAccessToken": "Musíte zadat přístupový token",
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/locale/da.json
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@
"components.Settings.SettingsUsers.defaultPermissions": "Standard Rettigheder",
"components.Settings.SettingsUsers.defaultPermissionsTip": "Initierende rettigheder som tildeles nye brugere",
"components.Settings.SettingsUsers.localLogin": "Aktivér Lokal Login",
"components.Settings.SettingsUsers.localLoginTip": "Tillad brugere at logge ind med deres email adresse og kodeord i stedet for Plex OAuth",
"components.Settings.SettingsUsers.localLoginTip": "Tillad brugere at logge ind med deres email adresse og kodeord",
"components.Settings.SettingsUsers.movieRequestLimitLabel": "Global Grænse For Filmforespørgsler",
"components.Settings.SettingsUsers.newPlexLoginTip": "Tillad {mediaServerName}-brugere at logge ind uden først at være importeret",
"components.Settings.SettingsUsers.toastSettingsFailure": "Noget gik galt i forsøget på at gemme indstillingerne.",
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/locale/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -689,7 +689,7 @@
"components.Settings.SettingsUsers.defaultPermissions": "Standardberechtigungen",
"components.Settings.SettingsUsers.defaultPermissionsTip": "Iniziale Berechtigungen für neue Nutzer",
"components.Settings.SettingsUsers.localLogin": "Lokale Anmeldung aktivieren",
"components.Settings.SettingsUsers.localLoginTip": "Berechtigt Nutzer sich über E-Mail und Passwort einzuloggen, statt Plex OAuth",
"components.Settings.SettingsUsers.localLoginTip": "Berechtigt Nutzer sich über E-Mail und Passwort einzuloggen",
"components.Settings.SettingsUsers.movieRequestLimitLabel": "Globales Filmanfragenlimit",
"components.Settings.SettingsUsers.newPlexLogin": "Aktiviere neuen {mediaServerName} Log-In",
"components.Settings.SettingsUsers.newPlexLoginTip": "Erlaube {mediaServerName} Nutzer Log-In, ohne diese zuerst importieren zu müssen",
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/locale/el.json
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,7 @@
"components.Settings.SettingsMain.locale": "Γλώσσα εμφάνισης",
"components.Settings.SettingsMain.originallanguageTip": "Φιλτράρετε το περιεχόμενο με βάση τη πρωτότυπη γλώσσα",
"components.Settings.SettingsMain.partialRequestsEnabled": "Επιτρέψτε τις αιτήσεων σειρών μερικώς",
"components.Settings.SettingsUsers.localLoginTip": "Χορήγηση άδειας σε χρήστες να συνδεθούν χρησιμοποιώντας το email τους και τον κωδικό τους, αντί να συνδεθούν μέσω Plex",
"components.Settings.SettingsUsers.localLoginTip": "Χορήγηση άδειας σε χρήστες να συνδεθούν χρησιμοποιώντας το email τους και τον κωδικό τους",
"components.Settings.externalUrl": "Εξωτερικό URL",
"components.Settings.tautulliApiKey": "Κλειδί API",
"components.Settings.toastTautulliSettingsFailure": "Κάτι πήγε στραβά κατά την αποθήκευση των ρυθμίσεων του Tautulli.",
Expand Down
Loading

0 comments on commit 4d7eba8

Please sign in to comment.