diff --git a/client/src/mobile/screens/admin/MAdminOidcCard.tsx b/client/src/mobile/screens/admin/MAdminOidcCard.tsx new file mode 100644 index 000000000..b9509571a --- /dev/null +++ b/client/src/mobile/screens/admin/MAdminOidcCard.tsx @@ -0,0 +1,66 @@ +import type { useAdmin } from '../../../pages/admin/useAdmin'; +import type { TranslationFn } from '../../../types'; +import { MAdminButton, MAdminCard, MAdminCardHead, MAdminField, MAdminInput } from './MAdminUi'; + +interface MAdminOidcCardProps { + admin: ReturnType; + t: TranslationFn; + onSave: () => void; +} + +/** OIDC / SSO, as on the desktop settings tab. The settings section leaves it out on a managed install. */ +export default function MAdminOidcCard({ admin, t, onSave }: MAdminOidcCardProps) { + const { oidcConfig, setOidcConfig, savingOidc } = admin; + return ( + + +
+ + setOidcConfig((c) => ({ ...c, display_name: e.target.value }))} + placeholder={t('admin.oidcDisplayNamePlaceholder')} + /> + + + setOidcConfig((c) => ({ ...c, issuer: e.target.value }))} + placeholder="https://accounts.google.com" + /> + + /.well-known/openid-configuration' })} + > + setOidcConfig((c) => ({ ...c, discovery_url: e.target.value }))} + placeholder="https://auth.example.com/application/o/trek/.well-known/openid-configuration" + /> + + + setOidcConfig((c) => ({ ...c, client_id: e.target.value }))} + /> + + + setOidcConfig((c) => ({ ...c, client_secret: e.target.value }))} + placeholder={oidcConfig.client_secret_set ? '••••••••' : ''} + /> + + + {t('common.save')} + +
+
+ ); +} diff --git a/client/src/mobile/screens/admin/MAdminSettingsSection.tsx b/client/src/mobile/screens/admin/MAdminSettingsSection.tsx index ba835e4de..9253d8ad2 100644 --- a/client/src/mobile/screens/admin/MAdminSettingsSection.tsx +++ b/client/src/mobile/screens/admin/MAdminSettingsSection.tsx @@ -6,6 +6,7 @@ import type { TranslationFn } from '../../../types' import type { useAdmin } from '../../../pages/admin/useAdmin' import MToggle from '../../components/MToggle' import { MBlockDisclosure, MProviderBlock, MTrekApiBlock } from './MApiProviderBlocks' +import MAdminOidcCard from './MAdminOidcCard' import MSetPickerSheet from '../settings/MSetPickerSheet' import { MAdminButton, @@ -31,7 +32,6 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio placesPhotosEnabled, placesAutocompleteEnabled, placesDetailsEnabled, placesEnrichEnabled, placesGoogleOnly, handleTogglePlacesGoogleOnly, placeShadowEnabled, - oidcConfig, setOidcConfig, savingOidc, passwordLogin, setPasswordLogin, passwordRegistration, setPasswordRegistration, oidcLogin, setOidcLogin, oidcRegistration, setOidcRegistration, envOverrideOidcOnly, oidcConfigured, requireMfa, @@ -388,55 +388,7 @@ export default function MAdminSettingsSection({ admin, t }: MAdminSettingsSectio )} {/* OIDC / SSO: the operator's on a managed install, as on the desktop tab. */} - {!managed && ( - - -
- - setOidcConfig((c) => ({ ...c, display_name: e.target.value }))} - placeholder={t('admin.oidcDisplayNamePlaceholder')} - /> - - - setOidcConfig((c) => ({ ...c, issuer: e.target.value }))} - placeholder="https://accounts.google.com" - /> - - /.well-known/openid-configuration' })}> - setOidcConfig((c) => ({ ...c, discovery_url: e.target.value }))} - placeholder="https://auth.example.com/application/o/trek/.well-known/openid-configuration" - /> - - - setOidcConfig((c) => ({ ...c, client_id: e.target.value }))} - /> - - - setOidcConfig((c) => ({ ...c, client_secret: e.target.value }))} - placeholder={oidcConfig.client_secret_set ? '••••••••' : ''} - /> - - - {t('common.save')} - -
-
- )} + {!managed && } {/* Danger zone */} diff --git a/server/src/nest/addons/addons.service.ts b/server/src/nest/addons/addons.service.ts index 675f405a4..b8868aef8 100644 --- a/server/src/nest/addons/addons.service.ts +++ b/server/src/nest/addons/addons.service.ts @@ -117,7 +117,7 @@ export class AddonsService { /** The photo providers users can connect: none while the journey addon is off. */ async enabledPhotoProviderIds(): Promise { if (!(await this.isAddonEnabled(ADDON_IDS.JOURNEY))) return []; - return (await this.photoProviders.listEnabled()).flatMap((row) => (row.id ? [row.id] : [])); + return this.photoProviders.listEnabledIds(); } /** wanderer may be used: Tours is on and the admin allowed it under Tours. */ diff --git a/server/tests/unit/nest/addons.service.test.ts b/server/tests/unit/nest/addons.service.test.ts index 53ff8c9c5..fb3695b0c 100644 --- a/server/tests/unit/nest/addons.service.test.ts +++ b/server/tests/unit/nest/addons.service.test.ts @@ -375,6 +375,15 @@ describe('AddonsService.list', () => { expect(listSpy).not.toHaveBeenCalled(); listSpy.mockRestore(); }); + + it('names the photo providers users can connect: the enabled ones, none while the journey addon is off', async () => { + await insertProvider({ id: 'immich', name: 'Immich', icon: 'image', enabled: 1 }); + await insertProvider({ id: 'off-provider', name: 'Off', icon: 'image', enabled: 0 }); + expect(await svc.enabledPhotoProviderIds()).toEqual([]); + + stubJourneyEnabled(); + expect(await svc.enabledPhotoProviderIds()).toEqual(['immich']); + }); }); // Direct coverage of the enablement reads/writers relocated from