mirror of
https://github.com/sct/overseerr.git
synced 2025-09-17 17:24:35 +02:00
feat: generate real api key
This also hides the api key from users without the ADMIN permission. It will not be returned from the api for them. Regenerate functionality is not in the commit.
This commit is contained in:
@@ -102,7 +102,7 @@ class Settings {
|
|||||||
this.data = {
|
this.data = {
|
||||||
clientId: '',
|
clientId: '',
|
||||||
main: {
|
main: {
|
||||||
apiKey: 'temp',
|
apiKey: '',
|
||||||
applicationUrl: '',
|
applicationUrl: '',
|
||||||
},
|
},
|
||||||
plex: {
|
plex: {
|
||||||
@@ -144,6 +144,10 @@ class Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get main(): MainSettings {
|
get main(): MainSettings {
|
||||||
|
if (!this.data.main.apiKey) {
|
||||||
|
this.data.main.apiKey = this.generateApiKey();
|
||||||
|
this.save();
|
||||||
|
}
|
||||||
return this.data.main;
|
return this.data.main;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,6 +204,16 @@ class Settings {
|
|||||||
return this.data.clientId;
|
return this.data.clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public regenerateApiKey(): MainSettings {
|
||||||
|
this.main.apiKey = this.generateApiKey();
|
||||||
|
this.save();
|
||||||
|
return this.main;
|
||||||
|
}
|
||||||
|
|
||||||
|
private generateApiKey(): string {
|
||||||
|
return Buffer.from(`${Date.now()}${this.clientId}`).toString('base64');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Settings Load
|
* Settings Load
|
||||||
*
|
*
|
||||||
|
@@ -4,6 +4,7 @@ import {
|
|||||||
RadarrSettings,
|
RadarrSettings,
|
||||||
SonarrSettings,
|
SonarrSettings,
|
||||||
Library,
|
Library,
|
||||||
|
MainSettings,
|
||||||
} from '../lib/settings';
|
} from '../lib/settings';
|
||||||
import { getRepository } from 'typeorm';
|
import { getRepository } from 'typeorm';
|
||||||
import { User } from '../entity/User';
|
import { User } from '../entity/User';
|
||||||
@@ -19,9 +20,15 @@ import { merge } from 'lodash';
|
|||||||
|
|
||||||
const settingsRoutes = Router();
|
const settingsRoutes = Router();
|
||||||
|
|
||||||
settingsRoutes.get('/main', (_req, res) => {
|
settingsRoutes.get('/main', (req, res) => {
|
||||||
const settings = getSettings();
|
const settings = getSettings();
|
||||||
|
|
||||||
|
if (!req.user?.hasPermission(Permission.ADMIN)) {
|
||||||
|
return res.status(200).json({
|
||||||
|
applicationUrl: settings.main.applicationUrl,
|
||||||
|
} as Partial<MainSettings>);
|
||||||
|
}
|
||||||
|
|
||||||
res.status(200).json(settings.main);
|
res.status(200).json(settings.main);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@ import { Form, Formik, Field } from 'formik';
|
|||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import Button from '../Common/Button';
|
import Button from '../Common/Button';
|
||||||
import { defineMessages, useIntl } from 'react-intl';
|
import { defineMessages, useIntl } from 'react-intl';
|
||||||
|
import { useUser, Permission } from '../../hooks/useUser';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
generalsettings: 'General Settings',
|
generalsettings: 'General Settings',
|
||||||
@@ -19,6 +20,7 @@ const messages = defineMessages({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const SettingsMain: React.FC = () => {
|
const SettingsMain: React.FC = () => {
|
||||||
|
const { hasPermission } = useUser();
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const { data, error, revalidate } = useSWR<MainSettings>(
|
const { data, error, revalidate } = useSWR<MainSettings>(
|
||||||
'/api/v1/settings/main'
|
'/api/v1/settings/main'
|
||||||
@@ -41,7 +43,6 @@ const SettingsMain: React.FC = () => {
|
|||||||
<div className="mt-6 sm:mt-5">
|
<div className="mt-6 sm:mt-5">
|
||||||
<Formik
|
<Formik
|
||||||
initialValues={{
|
initialValues={{
|
||||||
apiKey: data?.apiKey,
|
|
||||||
applicationUrl: data?.applicationUrl,
|
applicationUrl: data?.applicationUrl,
|
||||||
}}
|
}}
|
||||||
onSubmit={async (values) => {
|
onSubmit={async (values) => {
|
||||||
@@ -59,40 +60,42 @@ const SettingsMain: React.FC = () => {
|
|||||||
{({ isSubmitting }) => {
|
{({ isSubmitting }) => {
|
||||||
return (
|
return (
|
||||||
<Form>
|
<Form>
|
||||||
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5">
|
{hasPermission(Permission.ADMIN) && (
|
||||||
<label
|
<div className="sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5">
|
||||||
htmlFor="username"
|
<label
|
||||||
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
htmlFor="username"
|
||||||
>
|
className="block text-sm font-medium leading-5 text-gray-400 sm:mt-px sm:pt-2"
|
||||||
{intl.formatMessage(messages.apikey)}
|
>
|
||||||
</label>
|
{intl.formatMessage(messages.apikey)}
|
||||||
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
</label>
|
||||||
<div className="max-w-lg flex rounded-md shadow-sm">
|
<div className="mt-1 sm:mt-0 sm:col-span-2">
|
||||||
<input
|
<div className="max-w-lg flex rounded-md shadow-sm">
|
||||||
type="text"
|
<input
|
||||||
id="username"
|
type="text"
|
||||||
className="flex-1 form-input block w-full min-w-0 rounded-none rounded-l-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
|
id="apiKey"
|
||||||
value={data?.apiKey}
|
className="flex-1 form-input block w-full min-w-0 rounded-none rounded-l-md transition duration-150 ease-in-out sm:text-sm sm:leading-5 bg-gray-700 border border-gray-500"
|
||||||
readOnly
|
value={data?.apiKey}
|
||||||
/>
|
readOnly
|
||||||
<CopyButton textToCopy={data?.apiKey ?? ''} />
|
/>
|
||||||
<button className="-ml-px relative inline-flex items-center px-4 py-2 border border-gray-500 text-sm leading-5 font-medium rounded-r-md text-white bg-indigo-500 hover:bg-indigo-400 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
|
<CopyButton textToCopy={data?.apiKey ?? ''} />
|
||||||
<svg
|
<button className="-ml-px relative inline-flex items-center px-4 py-2 border border-gray-500 text-sm leading-5 font-medium rounded-r-md text-white bg-indigo-500 hover:bg-indigo-400 focus:outline-none focus:ring-blue focus:border-blue-300 active:bg-gray-100 active:text-gray-700 transition ease-in-out duration-150">
|
||||||
className="w-5 h-5"
|
<svg
|
||||||
fill="currentColor"
|
className="w-5 h-5"
|
||||||
viewBox="0 0 20 20"
|
fill="currentColor"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
viewBox="0 0 20 20"
|
||||||
>
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
<path
|
>
|
||||||
fillRule="evenodd"
|
<path
|
||||||
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
|
fillRule="evenodd"
|
||||||
clipRule="evenodd"
|
d="M4 2a1 1 0 011 1v2.101a7.002 7.002 0 0111.601 2.566 1 1 0 11-1.885.666A5.002 5.002 0 005.999 7H9a1 1 0 010 2H4a1 1 0 01-1-1V3a1 1 0 011-1zm.008 9.057a1 1 0 011.276.61A5.002 5.002 0 0014.001 13H11a1 1 0 110-2h5a1 1 0 011 1v5a1 1 0 11-2 0v-2.101a7.002 7.002 0 01-11.601-2.566 1 1 0 01.61-1.276z"
|
||||||
/>
|
clipRule="evenodd"
|
||||||
</svg>
|
/>
|
||||||
</button>
|
</svg>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5">
|
<div className="mt-6 sm:mt-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:items-start sm:border-t sm:border-gray-800 sm:pt-5">
|
||||||
<label
|
<label
|
||||||
htmlFor="name"
|
htmlFor="name"
|
||||||
|
@@ -6,7 +6,7 @@ import useRouteGuard from '../../hooks/useRouteGuard';
|
|||||||
import { Permission } from '../../hooks/useUser';
|
import { Permission } from '../../hooks/useUser';
|
||||||
|
|
||||||
const SettingsPage: NextPage = () => {
|
const SettingsPage: NextPage = () => {
|
||||||
useRouteGuard(Permission.MANAGE_USERS);
|
useRouteGuard(Permission.MANAGE_SETTINGS);
|
||||||
return (
|
return (
|
||||||
<SettingsLayout>
|
<SettingsLayout>
|
||||||
<SettingsMain />
|
<SettingsMain />
|
||||||
|
@@ -6,7 +6,7 @@ import { Permission } from '../../hooks/useUser';
|
|||||||
import useRouteGuard from '../../hooks/useRouteGuard';
|
import useRouteGuard from '../../hooks/useRouteGuard';
|
||||||
|
|
||||||
const SettingsMainPage: NextPage = () => {
|
const SettingsMainPage: NextPage = () => {
|
||||||
useRouteGuard(Permission.MANAGE_USERS);
|
useRouteGuard(Permission.MANAGE_SETTINGS);
|
||||||
return (
|
return (
|
||||||
<SettingsLayout>
|
<SettingsLayout>
|
||||||
<SettingsJobs />
|
<SettingsJobs />
|
||||||
|
@@ -6,7 +6,7 @@ import { Permission } from '../../hooks/useUser';
|
|||||||
import useRouteGuard from '../../hooks/useRouteGuard';
|
import useRouteGuard from '../../hooks/useRouteGuard';
|
||||||
|
|
||||||
const SettingsMainPage: NextPage = () => {
|
const SettingsMainPage: NextPage = () => {
|
||||||
useRouteGuard(Permission.MANAGE_USERS);
|
useRouteGuard(Permission.MANAGE_SETTINGS);
|
||||||
return (
|
return (
|
||||||
<SettingsLayout>
|
<SettingsLayout>
|
||||||
<SettingsMain />
|
<SettingsMain />
|
||||||
|
@@ -6,7 +6,7 @@ import { Permission } from '../../hooks/useUser';
|
|||||||
import useRouteGuard from '../../hooks/useRouteGuard';
|
import useRouteGuard from '../../hooks/useRouteGuard';
|
||||||
|
|
||||||
const PlexSettingsPage: NextPage = () => {
|
const PlexSettingsPage: NextPage = () => {
|
||||||
useRouteGuard(Permission.MANAGE_USERS);
|
useRouteGuard(Permission.MANAGE_SETTINGS);
|
||||||
return (
|
return (
|
||||||
<SettingsLayout>
|
<SettingsLayout>
|
||||||
<SettingsPlex />
|
<SettingsPlex />
|
||||||
|
@@ -6,7 +6,7 @@ import { Permission } from '../../hooks/useUser';
|
|||||||
import useRouteGuard from '../../hooks/useRouteGuard';
|
import useRouteGuard from '../../hooks/useRouteGuard';
|
||||||
|
|
||||||
const ServicesSettingsPage: NextPage = () => {
|
const ServicesSettingsPage: NextPage = () => {
|
||||||
useRouteGuard(Permission.MANAGE_USERS);
|
useRouteGuard(Permission.MANAGE_SETTINGS);
|
||||||
return (
|
return (
|
||||||
<SettingsLayout>
|
<SettingsLayout>
|
||||||
<SettingsServices />
|
<SettingsServices />
|
||||||
|
Reference in New Issue
Block a user