First somewhat working image
This commit is contained in:
parent
2fee5a8db7
commit
a8bedc24a4
@ -1,7 +1,8 @@
|
|||||||
import { createAuthFromSettings } from "utils/auth/auth-helpers";
|
import { createAuthProvider } from "utils/auth/auth-helpers";
|
||||||
import { bookmarksResponse } from "utils/config/api-response";
|
import { bookmarksResponse } from "utils/config/api-response";
|
||||||
|
import { getSettings } from "utils/config/config";
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
const auth = createAuthFromSettings()
|
const auth = createAuthProvider(getSettings())
|
||||||
res.send(await bookmarksResponse(auth.permissions(req)));
|
res.send(await bookmarksResponse(auth.permissions(req)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,14 @@
|
|||||||
import { createAuthFromSettings } from "utils/auth/auth-helpers";
|
import { createAuthProvider } from "utils/auth/auth-helpers";
|
||||||
import { servicesResponse } from "utils/config/api-response";
|
import { servicesResponse } from "utils/config/api-response";
|
||||||
|
import { getSettings } from "utils/config/config";
|
||||||
|
import createLogger from "utils/logger";
|
||||||
|
|
||||||
|
let logger = createLogger("services_index")
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
const auth = createAuthFromSettings()
|
logger.log("Call services");
|
||||||
|
const auth = createAuthProvider(getSettings)
|
||||||
res.send(await servicesResponse(auth.permissions(req)));
|
const result = await servicesResponse(auth.permissions(req))
|
||||||
|
logger.log(result);
|
||||||
|
res.send(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { createAuthFromSettings } from "utils/auth/auth-helpers";
|
import { createAuthProvider } from "utils/auth/auth-helpers";
|
||||||
import { widgetsResponse } from "utils/config/api-response";
|
import { widgetsResponse } from "utils/config/api-response";
|
||||||
|
import { getSettings } from "utils/config/config";
|
||||||
|
|
||||||
export default async function handler(req, res) {
|
export default async function handler(req, res) {
|
||||||
const auth = createAuthFromSettings();
|
const auth = createAuthProvider(getSettings());
|
||||||
|
|
||||||
res.send(await widgetsResponse(auth.permissions(req)));
|
res.send(await widgetsResponse(auth.permissions(req)));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
/* eslint-disable react/no-array-index-key */
|
/* eslint-disable react/no-array-index-key */
|
||||||
import useSWR, { SWRConfig } from "swr";
|
import useSWR, { unstable_serialize, SWRConfig } from "swr";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
import {headers} from "next/header";
|
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
import { useTranslation } from "next-i18next";
|
import { useTranslation } from "next-i18next";
|
||||||
@ -28,8 +27,8 @@ import ErrorBoundary from "components/errorboundry";
|
|||||||
import themes from "utils/styles/themes";
|
import themes from "utils/styles/themes";
|
||||||
import QuickLaunch from "components/quicklaunch";
|
import QuickLaunch from "components/quicklaunch";
|
||||||
import { getStoredProvider, searchProviders } from "components/widgets/search/search";
|
import { getStoredProvider, searchProviders } from "components/widgets/search/search";
|
||||||
import { NullPermissions, createAuthFromSettings } from "utils/auth/auth-helpers";
|
import { createAuthorizer, fetchWithAuth } from "utils/auth/auth-helpers";
|
||||||
|
import { NullAuthProvider } from "utils/auth/null";
|
||||||
const ThemeToggle = dynamic(() => import("components/toggles/theme"), {
|
const ThemeToggle = dynamic(() => import("components/toggles/theme"), {
|
||||||
ssr: false,
|
ssr: false,
|
||||||
});
|
});
|
||||||
@ -44,25 +43,28 @@ const Version = dynamic(() => import("components/version"), {
|
|||||||
|
|
||||||
const rightAlignedWidgets = ["weatherapi", "openweathermap", "weather", "openmeteo", "search", "datetime"];
|
const rightAlignedWidgets = ["weatherapi", "openweathermap", "weather", "openmeteo", "search", "datetime"];
|
||||||
|
|
||||||
export async function getStaticProps() {
|
export async function getServerSideProps({req}) {
|
||||||
let logger;
|
let logger;
|
||||||
try {
|
try {
|
||||||
logger = createLogger("index");
|
logger = createLogger("index");
|
||||||
const { providers, auth, ...settings } = getSettings();
|
const { providers, auth, ...settings } = getSettings();
|
||||||
|
const authProvider = createAuthorizer({auth: auth});
|
||||||
|
|
||||||
const services = await servicesResponse(NullPermissions);
|
const services = await servicesResponse(authProvider.authorize(req));
|
||||||
const bookmarks = await bookmarksResponse(NullPermissions);
|
const bookmarks = await bookmarksResponse(authProvider.authorize(req));
|
||||||
const widgets = await widgetsResponse(NullPermissions);
|
const widgets = await widgetsResponse(authProvider.authorize(req));
|
||||||
|
const authContext = authProvider.getContext(req);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
initialSettings: settings,
|
initialSettings: settings,
|
||||||
fallback: {
|
fallback: {
|
||||||
"/api/services": services,
|
[unstable_serialize(["/api/services", authContext])]: services,
|
||||||
"/api/bookmarks": bookmarks,
|
[unstable_serialize(["/api/bookmarks", authContext])]: bookmarks,
|
||||||
"/api/widgets": widgets,
|
[unstable_serialize(["/api/widgets", authContext])]: widgets,
|
||||||
"/api/hash": false,
|
"/api/hash": false,
|
||||||
},
|
},
|
||||||
|
authContext: authContext,
|
||||||
...(await serverSideTranslations(settings.language ?? "en")),
|
...(await serverSideTranslations(settings.language ?? "en")),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -70,22 +72,24 @@ export async function getStaticProps() {
|
|||||||
if (logger) {
|
if (logger) {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
|
const authContext = NullAuthProvider.create().getContext(req);
|
||||||
return {
|
return {
|
||||||
props: {
|
props: {
|
||||||
initialSettings: {},
|
initialSettings: {},
|
||||||
fallback: {
|
fallback: {
|
||||||
"/api/services": [],
|
[unstable_serialize(["/api/services", authContext])]: [],
|
||||||
"/api/bookmarks": [],
|
[unstable_serialize(["/api/bookmarks", authContext])]: [],
|
||||||
"/api/widgets": [],
|
[unstable_serialize(["/api/widgets", authContext])]: [],
|
||||||
"/api/hash": false,
|
"/api/hash": false,
|
||||||
},
|
},
|
||||||
|
authContext: authContext,
|
||||||
...(await serverSideTranslations("en")),
|
...(await serverSideTranslations("en")),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function Index({ initialSettings, fallback }) {
|
function Index({ initialSettings, fallback, authContext }) {
|
||||||
const windowFocused = useWindowFocus();
|
const windowFocused = useWindowFocus();
|
||||||
const [stale, setStale] = useState(false);
|
const [stale, setStale] = useState(false);
|
||||||
const { data: errorsData } = useSWR("/api/validate");
|
const { data: errorsData } = useSWR("/api/validate");
|
||||||
@ -155,7 +159,7 @@ function Index({ initialSettings, fallback }) {
|
|||||||
return (
|
return (
|
||||||
<SWRConfig value={{ fallback, fetcher: (resource, init) => fetch(resource, init).then((res) => res.json()) }}>
|
<SWRConfig value={{ fallback, fetcher: (resource, init) => fetch(resource, init).then((res) => res.json()) }}>
|
||||||
<ErrorBoundary>
|
<ErrorBoundary>
|
||||||
<Home initialSettings={initialSettings} />
|
<Home initialSettings={initialSettings} authContext={authContext}/>
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</SWRConfig>
|
</SWRConfig>
|
||||||
);
|
);
|
||||||
@ -169,7 +173,7 @@ const headerStyles = {
|
|||||||
boxedWidgets: "m-6 mb-0 sm:m-9 sm:mb-0 sm:mt-1",
|
boxedWidgets: "m-6 mb-0 sm:m-9 sm:mb-0 sm:mt-1",
|
||||||
};
|
};
|
||||||
|
|
||||||
function Home({ initialSettings }) {
|
function Home({ initialSettings, authContext }) {
|
||||||
const { i18n } = useTranslation();
|
const { i18n } = useTranslation();
|
||||||
const { theme, setTheme } = useContext(ThemeContext);
|
const { theme, setTheme } = useContext(ThemeContext);
|
||||||
const { color, setColor } = useContext(ColorContext);
|
const { color, setColor } = useContext(ColorContext);
|
||||||
@ -181,11 +185,9 @@ function Home({ initialSettings }) {
|
|||||||
setSettings(initialSettings);
|
setSettings(initialSettings);
|
||||||
}, [initialSettings, setSettings]);
|
}, [initialSettings, setSettings]);
|
||||||
|
|
||||||
const auth = createAuthFromSettings();
|
const { data: services } = useSWR(["/api/services", authContext], fetchWithAuth);
|
||||||
|
const { data: bookmarks } = useSWR(["/api/bookmarks", authContext], fetchWithAuth);
|
||||||
const { data: services } = useSWR(auth.cacheContext("/api/services"), auth.fetcher);
|
const { data: widgets } = useSWR(["/api/widgets", authContext], fetchWithAuth);
|
||||||
const { data: bookmarks } = useSWR(auth.cacheContext("/api/bookmarks"), auth.fetcher);
|
|
||||||
const { data: widgets } = useSWR(auth.cacheContext("/api/widgets"), auth.fetcher);
|
|
||||||
|
|
||||||
const servicesAndBookmarks = [
|
const servicesAndBookmarks = [
|
||||||
...services.map((sg) => sg.services).flat(),
|
...services.map((sg) => sg.services).flat(),
|
||||||
@ -470,7 +472,7 @@ function Home({ initialSettings }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Wrapper({ initialSettings, fallback }) {
|
export default function Wrapper({ initialSettings, fallback, authContext }) {
|
||||||
const wrappedStyle = {};
|
const wrappedStyle = {};
|
||||||
let backgroundBlur = false;
|
let backgroundBlur = false;
|
||||||
let backgroundSaturate = false;
|
let backgroundSaturate = false;
|
||||||
@ -521,7 +523,7 @@ export default function Wrapper({ initialSettings, fallback }) {
|
|||||||
backgroundBrightness && `backdrop-brightness-${initialSettings.background.brightness}`,
|
backgroundBrightness && `backdrop-brightness-${initialSettings.background.brightness}`,
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Index initialSettings={initialSettings} fallback={fallback} />
|
<Index initialSettings={initialSettings} fallback={fallback} authContext={authContext}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,41 +1,42 @@
|
|||||||
import { getSettings } from "utils/config/config";
|
import { ProxyAuthProvider} from "./proxy";
|
||||||
import { ProxyAuthKey, createProxyAuth } from "./proxy";
|
import { NullAuthProvider} from "./null";
|
||||||
|
|
||||||
export const NullPermissions = { user: null, groups:[]}
|
const AuthProviders = {
|
||||||
|
NullAuthProvider,
|
||||||
|
ProxyAuthProvider
|
||||||
|
};
|
||||||
|
|
||||||
export const NullAuth = {
|
function getProviderByKey(key) {
|
||||||
permissions: (request) => NullPermissions,
|
return AuthProviders.find((provider) => provider.key == key) ?? NullAuthProvider;
|
||||||
cacheContext: (key) => key,
|
|
||||||
fetcher: (key) => fetch(key).then((res) => res.json())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createAuthFromSettings() {
|
export function createAuthorizer({auth}) {
|
||||||
const {auth} = getSettings();
|
|
||||||
if (auth) {
|
if (auth) {
|
||||||
switch (Object.keys(auth)[0]) {
|
getProviderByKey(Object.keys(auth)[0]).create(auth[ProxyAuthKey]);
|
||||||
case ProxyAuthKey:
|
|
||||||
return createProxyAuth(auth[ProxyAuthKey]);
|
|
||||||
default:
|
|
||||||
return NullAuth;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return NullAuth
|
return NullAuthProvider.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchWithAuth(key, context) {
|
||||||
|
return getProviderByKey(context.provider).fetch([key, context]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const filterAllowedServices = (perms, services) => filterAllowedItems(perms, services, 'services');
|
export const filterAllowedServices = (perms, services) => filterAllowedItems(perms, services, 'services');
|
||||||
export const filterAllowedBookmarks = (perms, bookmarks) => filterAllowedItems(perms, bookmarks, 'bookmarks');
|
export const filterAllowedBookmarks = (perms, bookmarks) => filterAllowedItems(perms, bookmarks, 'bookmarks');
|
||||||
export const filterAllowedWidgets = (perms, widgets) => filterAllowedItems(perms, widgets, 'widgets')
|
export const filterAllowedWidgets = (perms, widgets) => {
|
||||||
|
return widgets.filter((widget) => authItemFilter(perms, widget.options) )
|
||||||
|
}
|
||||||
|
|
||||||
function filterAllowedItems({user, groups}, itemGroups, groupKey) {
|
function filterAllowedItems({user, groups}, itemGroups, groupKey) {
|
||||||
return itemGroups.map((group) => ({
|
return itemGroups.map((group) => ({
|
||||||
name: group.name,
|
name: group.name,
|
||||||
[groupKey]: group[groupKey].filter((item) => authItemFilter({user, groups}, item))
|
[groupKey]: group[groupKey].filter((item) => authItemFilter({user, groups}, item))
|
||||||
})).filter((group) => !group[groupKey].length)
|
})).filter((group) => !group[groupKey].length);
|
||||||
}
|
}
|
||||||
|
|
||||||
function authItemFilter({user, groups}, item) {
|
function authItemFilter({user, groups}, item) {
|
||||||
const groupAllow = (!(allowGroups in item)) || groups.some(group => item.allowGroups.includes(group));
|
const groupAllow = (!('allowGroups' in item)) || groups.some(group => item.allowGroups.includes(group));
|
||||||
const userAllow = (!(allowUsers in item)) || item.allowUsers.includes(user);
|
const userAllow = (!('allowUsers' in item)) || item.allowUsers.includes(user);
|
||||||
|
|
||||||
return userAllow || groupAllow;
|
return userAllow || groupAllow;
|
||||||
}
|
}
|
||||||
|
|||||||
21
src/utils/auth/null.js
Normal file
21
src/utils/auth/null.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
const NullPermissions = { user: null, groups:[]}
|
||||||
|
const NullAuthKey = "none"
|
||||||
|
|
||||||
|
function createNullAuth() {
|
||||||
|
return {
|
||||||
|
authorize: (request) => NullPermissions,
|
||||||
|
getContext: (request) => { return {
|
||||||
|
provider: NullAuthKey
|
||||||
|
} },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchNullAuth([key, context]) {
|
||||||
|
return fetch(key).then((res) => res.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NullAuthProvider = {
|
||||||
|
key: NullAuthKey,
|
||||||
|
create: createNullAuth,
|
||||||
|
fetch: fetchNullAuth
|
||||||
|
}
|
||||||
@ -1,42 +1,35 @@
|
|||||||
// Proxy auth is meant to be used by a reverse proxy that injects permission headers into the origin
|
// Proxy auth is meant to be used by a reverse proxy that injects permission headers into the origin
|
||||||
// request. In this case we are relying on our proxy to authenitcate our users and validate.
|
// request. In this case we are relying on our proxy to authenitcate our users and validate.
|
||||||
import {createLogger} from "utils/logger";
|
const ProxyAuthKey="proxy_auth"
|
||||||
import { headers } from 'next/headers';
|
|
||||||
|
|
||||||
export const ProxyAuthKey="proxy_auth"
|
|
||||||
|
|
||||||
|
|
||||||
function getProxyPermissions(userHeader, groupHeader, request) {
|
function getProxyPermissions(userHeader, groupHeader, request) {
|
||||||
const logger = createLogger("proxyAuth")
|
|
||||||
const user = (userHeader)?request.headers.get(userHeader):None;
|
const user = (userHeader)?request.headers.get(userHeader):None;
|
||||||
if (!user) {
|
|
||||||
logger.debug("unable to retreive user. User header doesn't exist or unspecified.")
|
|
||||||
}
|
|
||||||
const groupsString = (groupHeader)?request.headers.get(groupHeader):"";
|
const groupsString = (groupHeader)?request.headers.get(groupHeader):"";
|
||||||
if (!groupsString) {
|
|
||||||
logger.debug("unable to retrieve groups. Groups header doesn't exist or unspecified")
|
|
||||||
}
|
|
||||||
|
|
||||||
return {user: user, groups: (groupsString)?groupsString.split(",").map((v) => v.trimStart()):[]}
|
return {user: user, groups: (groupsString)?groupsString.split(",").map((v) => v.trimStart()):[]}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createProxyAuth({groupHeader, userHeader}) {
|
function createProxyAuth({groupHeader, userHeader}) {
|
||||||
const logger = createLogger("proxyAuth")
|
|
||||||
|
|
||||||
if (!userHeader) {
|
|
||||||
logger.debug("'userHeader' value not specified");
|
|
||||||
}
|
|
||||||
if (!groupHeader) {
|
|
||||||
logger.debug("'groupHeader' value not specified")
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
permissions : (request) => getProxyPermissions(userHeader, groupHeader, request),
|
getContext : (request) => {
|
||||||
cacheContext: (key) => [ key, {
|
return {
|
||||||
...userHeader && {[userHeader]: headers.get(userHeader) },
|
provider: ProxyAuthKey,
|
||||||
...groupHeader && {[groupHeader]: headers.get(groupHeader)}
|
headers: {
|
||||||
}],
|
...userHeader && {[userHeader]: request.headers.get(userHeader) },
|
||||||
fetcher: ([key, context]) => {
|
...groupHeader && {[groupHeader]: request.headers.get(groupHeader)}
|
||||||
fetch(key, {headers: context}).then((res) => res.json())
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
authorize : (request) => getProxyPermissions(userHeader, groupHeader, request)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchProxyAuth([key, context]) {
|
||||||
|
return fetch(key, {headers: context.headers}).then((res) => res.json())
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ProxyAuthProvider = {
|
||||||
|
key: ProxyAuthKey,
|
||||||
|
create: createProxyAuth,
|
||||||
|
fetch: fetchProxyAuth
|
||||||
}
|
}
|
||||||
@ -83,7 +83,7 @@ export async function widgetsResponse(perms) {
|
|||||||
let configuredWidgets;
|
let configuredWidgets;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
configuredWidgets = filterAllowedWidgets(perms, cleanWidgetGroups(await widgetsFromConfig()));
|
configuredWidgets = filterAllowedWidgets(perms, await cleanWidgetGroups(await widgetsFromConfig()));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Failed to load widgets, please check widgets.yaml for errors or remove example entries.");
|
console.error("Failed to load widgets, please check widgets.yaml for errors or remove example entries.");
|
||||||
if (e) console.error(e);
|
if (e) console.error(e);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user