diff --git a/components/nav.ts b/components/nav.ts index 28496698..107aaee3 100644 --- a/components/nav.ts +++ b/components/nav.ts @@ -1,13 +1,9 @@ -import bbnLogo from '../assets/img/bbnBig.svg'; -import bbnMusicLogo from '../assets/img/bbnMusicBig.svg'; -import bbnHostingLogo from '../assets/img/bbnHosting.svg'; -import bbnAdminLogo from '../assets/img/bbnAdmin.svg'; - import '../assets/css/components/nav.css'; -import { Box, Button, ButtonStyle, CenterV, Color, Component, createElement, Custom, Horizontal, Icon, img, MaterialIcons, PlainText, Spacer, Vertical } from "webgen/mod.ts"; -import { IsLoggedIn, stringToColour } from "../pages/manager/helper.ts"; +import { Box, Button, ButtonStyle, CenterV, Color, Component, createElement, Custom, Horizontal, Icon, img, MaterialIcons, PlainText, Reactive, Spacer, Vertical } from "webgen/mod.ts"; +import { activeUser, IsLoggedIn, permCheck, stringToColour } from "../pages/manager/helper.ts"; import { delay } from "https://deno.land/std@0.167.0/async/delay.ts"; import { API } from "../pages/manager/RESTSpec.ts"; +import { activeLogo, pages } from "./pages.ts"; new MaterialIcons(); const Nav = (component: Component) => { const nav = createElement("nav"); @@ -30,26 +26,17 @@ function ProfilePicture(component: Component, name: string) { ele.style.backgroundColor = stringToColour(name); return Custom(ele).addClass("profile-picture"); } -const dropOver = Box(Vertical( + +const dropOver = Reactive(activeUser, "permission", () => Vertical( PlainText("SWITCH TO").addClass("title"), - Horizontal( - Custom(img(bbnLogo)), + pages.map(([ logo, permission, route ]) => permCheck(...permission) ? Horizontal( + Custom(img(logo)), Spacer(), Icon("arrow_forward_ios") - ).addClass("small-entry") - .onClick(() => location.href = "/"), - Horizontal( - Custom(img(bbnMusicLogo)), - Spacer(), - Icon("arrow_forward_ios") - ).addClass("small-entry") - .onClick(() => location.href = "/music"), - (API.permission.isReviewer(IsLoggedIn()) ? Horizontal( - Custom(img(bbnAdminLogo)), - Spacer(), - Icon("arrow_forward_ios") - ).addClass("small-entry") - .onClick(() => location.href = "/admin") : null), + ) + .addClass("small-entry") + .onClick(() => location.href = route) : null + ), Horizontal( PlainText("Go to Settings"), Spacer(), @@ -57,7 +44,10 @@ const dropOver = Box(Vertical( ).addClass("small-entry", "settings") .onClick(() => location.href = "/settings") ) -).addClass("drop-over").setId("drop-over").draw(); +) + .addClass("drop-over") + .setId("drop-over") + .draw(); dropOver.onblur = () => dropOver.classList.remove("open"); dropOver.tabIndex = 0; @@ -82,15 +72,7 @@ export function DynaNavigation(type: "Home" | "Music" | "Settings" | "Hosting" | Icon("apps"), Vertical( Custom(img( - (() => { - if (type == "Music") - return bbnMusicLogo; - if (type == "Hosting") - return bbnHostingLogo; - if (type == "Admin") - return bbnAdminLogo; - return bbnLogo; - })() + activeLogo(type) )), ), ) diff --git a/components/pages.ts b/components/pages.ts new file mode 100644 index 00000000..f56a9e07 --- /dev/null +++ b/components/pages.ts @@ -0,0 +1,27 @@ +// @deno-types="https://raw.githubusercontent.com/lucsoft-DevTeam/lucsoft.de/master/custom.d.ts" +import bbnHolding from '../assets/img/bbnHolding.svg'; +// @deno-types="https://raw.githubusercontent.com/lucsoft-DevTeam/lucsoft.de/master/custom.d.ts" +import bbnMusicLogo from '../assets/img/bbnMusic.svg'; +// @deno-types="https://raw.githubusercontent.com/lucsoft-DevTeam/lucsoft.de/master/custom.d.ts" +import bbnHostingLogo from '../assets/img/bbnHosting.svg'; +// @deno-types="https://raw.githubusercontent.com/lucsoft-DevTeam/lucsoft.de/master/custom.d.ts" +import bbnAdminLogo from '../assets/img/bbnAdmin.svg'; + +import { Permission } from "../pages/manager/RESTSpec.ts"; + +export const pages: [ logo: any, perm: Permission[], route: string ][] = [ + [ bbnHolding, [], "/" ], + [ bbnMusicLogo, [], "/music" ], + [ bbnHostingLogo, [ "/bbn/beta-hosting" ], "/hosting" ], + [ bbnAdminLogo, [ "/bbn/manage", "/hmsys/user" ], "/admin" ], +]; + +export function activeLogo(type: string) { + if (type == "Music") + return bbnMusicLogo; + if (type == "Hosting") + return bbnHostingLogo; + if (type == "Admin") + return bbnAdminLogo; + return bbnHolding; +} \ No newline at end of file diff --git a/pages/admin/admin.ts b/pages/admin/admin.ts index 2b2d75e3..4db5326b 100644 --- a/pages/admin/admin.ts +++ b/pages/admin/admin.ts @@ -1,8 +1,7 @@ -import { WebGen, MaterialIcons, Box, Custom, Vertical, View, ViewClass, loadingWheel, PlainText } from "webgen/mod.ts"; -import { IsLoggedIn, Redirect, RegisterAuthRefresh, activeUser, renewAccessTokenIfNeeded } from "../manager/helper.ts"; +import { WebGen, MaterialIcons, Box, Custom, Vertical, View, ViewClass, loadingWheel } from "webgen/mod.ts"; +import { Redirect, RegisterAuthRefresh, activeUser, permCheck, renewAccessTokenIfNeeded } from "../manager/helper.ts"; import { changeThemeColor } from "../manager/misc/common.ts"; import { DynaNavigation } from "../../components/nav.ts"; -import { API } from "../manager/RESTSpec.ts"; import { ActionBar } from "../manager/misc/actionbar.ts"; import { ViewState } from "../admin/types.ts"; import { ReviewPanel } from "./reviews.ts"; @@ -16,7 +15,10 @@ import { OverviewPanel } from "./overview.ts"; Redirect(); await RegisterAuthRefresh(); -if (!API.permission.isReviewer(IsLoggedIn())) { +if (!permCheck( + "/hmsys/user/manage", + "/bbn/manage" +)) { location.href = "/"; } @@ -57,11 +59,11 @@ const view: ViewClass = View(({ state, update }) => Vertic return ReviewPanel(() => view, state); if (state.users && state.users.length != 0 && state.type == "users") return UserPanel(state); - if (state.type == "payouts") - return PayoutPanel(state) + if (state.type == "payouts") + return PayoutPanel(state); if (state.type == "overview") - return OverviewPanel(state) - return Custom(loadingWheel() as Element as HTMLElement) + return OverviewPanel(state); + return Custom(loadingWheel() as Element as HTMLElement); })()).addClass("loading"), )) .change(({ update }) => { diff --git a/pages/admin/helper.ts b/pages/admin/helper.ts index 51ace7a6..f9055536 100644 --- a/pages/admin/helper.ts +++ b/pages/admin/helper.ts @@ -1,24 +1,24 @@ import { ViewClass, Component, Custom, PlainText, ReCache, Image, Box } from "webgen/mod.ts"; import { API } from "../manager/RESTSpec.ts"; -import { IsLoggedIn, ProfileData, stringToColour } from "../manager/helper.ts"; +import { ProfileData, permCheck, stringToColour } from "../manager/helper.ts"; import { ViewState } from "./types.ts"; export async function loadReviews(view: ViewClass) { - if (API.permission.isReviewer(IsLoggedIn())) { + if (permCheck("/bbn/manage/drops/review")) { const list = await API.music(API.getToken()).reviews.get(); view.viewOptions().update({ reviews: list }); } } export async function loadUsers(view: ViewClass) { - if (API.permission.isAdmin(IsLoggedIn())) { + if (permCheck("/hmsys/user")) { const list = await API.user(API.getToken()).list.get(); view.viewOptions().update({ users: list }); } } export async function loadPayouts(view: ViewClass) { - if (API.permission.isAdmin(IsLoggedIn())) { + if (permCheck("/bbn/manage/drops/review")) { const list = await API.music(API.getToken()).payouts.get(); view.viewOptions().update({ payouts: list }); } diff --git a/pages/index/index.ts b/pages/index/index.ts index ca2dce14..5e2a66b4 100644 --- a/pages/index/index.ts +++ b/pages/index/index.ts @@ -6,8 +6,10 @@ import { renderFooter } from "../../components/footer.ts"; import { asset } from "../../assets/img/subsidiaries/index.ts"; import '../../assets/css/components/subsidiaries.css'; import services from "../../data/services.json" assert { type: "json" }; +import { RegisterAuthRefresh } from "../manager/helper.ts"; WebGen({ icon: new MaterialIcons() }); +await RegisterAuthRefresh(); function inlineSVG(data: string) { const ele = createElement("div"); diff --git a/pages/manager/RESTSpec.ts b/pages/manager/RESTSpec.ts index 10331841..06ee8dfd 100644 --- a/pages/manager/RESTSpec.ts +++ b/pages/manager/RESTSpec.ts @@ -1,8 +1,6 @@ // deno-lint-ignore-file no-unused-vars import { assert } from "https://deno.land/std@0.167.0/testing/asserts.ts"; -import "https://unpkg.com/construct-style-sheets-polyfill@3.1.0/dist/adoptedStyleSheets.js"; import { Drop, DropType, Payout } from "../../spec/music.ts"; -import { ProfileData } from "./helper.ts"; export type ErrorObject = { error: true, @@ -12,18 +10,45 @@ export type ErrorObject = { // Only visable when running in verbose stack?: string; }; +export const Permissions = [ + "/hmsys", + "/hmsys/user", + "/hmsys/user/manage", + + "/bbn", + "/bbn/beta-hosting", + "/bbn/manage", + "/bbn/manage/drops", + "/bbn/manage/drops/review", + "/bbn/manage/payouts", +] as const; + +export type Permission = typeof Permissions[ number ]; + export const API = { getToken: () => localStorage[ "access-token" ], BASE_URL: location.hostname == "bbn.one" ? "https://bbn.one/api/@bbn/" : "http://localhost:8443/api/@bbn/", // deno-lint-ignore no-explicit-any isError: (data: any): data is ErrorObject => typeof data === "object" && data.error, - permission: { - consts: { - admin: "6293b146d55350d24e6da542", - reviewer: "6293bb4fd55350d24e6da550", - }, - isReviewer: (x: ProfileData | null) => (x?.groups ?? []).find(x => API.permission.consts.admin == x || API.permission.consts.reviewer == x), - isAdmin: (x: ProfileData | null) => (x?.groups ?? []).find(x => API.permission.consts.admin == x), + permission: Permissions, + _legacyPermissionFromGroups: (group: string) => { + const admin = "6293b146d55350d24e6da542"; + const reviewer = "6293bb4fd55350d24e6da550"; + if (group === reviewer) + return [ + "/bbn/payouts" + ]; + + if (group === admin) + // Always highest permissions + return [ + "/bbn", + "/hmsys" + ]; + return []; + }, + isPermited: (requiredPermissions: Permission[], userPermission: Permission[]) => { + return requiredPermissions.every(required => userPermission.find(user => required.startsWith(user))); }, user: (token: string) => ({ mail: { diff --git a/pages/manager/helper.ts b/pages/manager/helper.ts index 79be1d23..fdb67ce4 100644 --- a/pages/manager/helper.ts +++ b/pages/manager/helper.ts @@ -1,7 +1,7 @@ // This code Will be ported to webgen import { Box, Button, ColumEntry, Component, Custom, Dialog, DropDownInput, Horizontal, Image, Page, PlainText, Reactive, ReCache, Spacer, State, StateHandler, Table, TextInput, Vertical, ViewClass } from "webgen/mod.ts"; -import { API } from "./RESTSpec.ts"; +import { API, Permission } from "./RESTSpec.ts"; import artwork from "../../assets/img/template-artwork.png"; import { Artist, ArtistTypes, Drop } from "../../spec/music.ts"; import { ViewState } from "./types.ts"; @@ -58,19 +58,35 @@ function rawAccessToken() { return JSON.parse(b64DecodeUnicode(localStorage[ "access-token" ].split(".")[ 1 ])); } + export const activeUser = State({ email: "--", username: "--", - avatar: undefined + avatar: undefined, + permission: [] }); +export function permCheck(...per: Permission[]) { + console.log("permitted", API.isPermited(per, activeUser.permission), "req", per, "has", [ ...activeUser.permission ]); + return API.isPermited(per, activeUser.permission); +} + export function updateActiveUserData() { try { - if (!localStorage.getItem("access-token")) return; - const user = JSON.parse(b64DecodeUnicode(localStorage[ "access-token" ].split(".")[ 1 ])).user as ProfileData; + console.log(activeUser.permission); + const user = IsLoggedIn(); + if (!user) return; activeUser.username = user.profile.username; activeUser.email = user.profile.email; activeUser.avatar = user.profile.avatar; + + // Convert id based system to new hmsys permission system. + activeUser.permission = State([ + ...activeUser.permission, + ...new Set(user.groups.map(x => API._legacyPermissionFromGroups(x)).flat()) + ]); + + console.log(activeUser.permission); } catch (_) { // Session should be invalid logOut(); @@ -119,6 +135,8 @@ function isExpired(exp: number) { export async function RegisterAuthRefresh() { try { + if (!IsLoggedIn()) return; + updateActiveUserData(); checkIfRefreshTokenIsValid(); await renewAccessTokenIfNeeded(); diff --git a/pages/manager/music/changeMain.ts b/pages/manager/music/changeMain.ts index cb7224bc..af3ae5f3 100644 --- a/pages/manager/music/changeMain.ts +++ b/pages/manager/music/changeMain.ts @@ -1,6 +1,6 @@ import { Grid, Horizontal, Page, PlainText, Spacer, Vertical, Wizard } from "webgen/mod.ts"; import { Drop, DropType } from "../../../spec/music.ts"; -import { IsLoggedIn, showPreviewImage } from "../helper.ts"; +import { permCheck, showPreviewImage } from "../helper.ts"; import { ActionBar } from "../misc/actionbar.ts"; import { changePage } from "../misc/common.ts"; import { DownloadDrop } from "../misc/drop.ts"; @@ -63,6 +63,6 @@ export function ChangeMain(data: Drop, update: (data: Partial) => const Permissions = { canTakedown: (drop: Drop) => drop.type == "PUBLISHED", canSubmit: (drop: Drop) => ([ "UNSUBMITTED", "PRIVATE" ]).includes(drop.type), - canEdit: (drop: Drop) => (drop.type == "PRIVATE" || drop.type == "UNSUBMITTED") || API.permission.isReviewer(IsLoggedIn()), + canEdit: (drop: Drop) => (drop.type == "PRIVATE" || drop.type == "UNSUBMITTED") || permCheck("/bbn/manage/drops"), canCancelReview: (drop: Drop) => drop.type == "UNDER_REVIEW" }; \ No newline at end of file diff --git a/serve.ts b/serve.ts index f47d3760..1d37b43b 100644 --- a/serve.ts +++ b/serve.ts @@ -35,6 +35,7 @@ serve({ "admin": "./pages/admin/admin.ts", }, poylfills: [ - "https://unpkg.com/construct-style-sheets-polyfill@3.1.0" + "https://unpkg.com/construct-style-sheets-polyfill@3.1.0", + "https://unpkg.com/urlpattern-polyfill@7.0.0" ] }); \ No newline at end of file