import { DEFAULT_CONFIG } from "@goauthentik/common/api/config"; import { EVENT_API_DRAWER_TOGGLE, EVENT_NOTIFICATION_DRAWER_TOGGLE, EVENT_WS_MESSAGE, } from "@goauthentik/common/constants"; import { globalAK } from "@goauthentik/common/global"; import { configureSentry } from "@goauthentik/common/sentry"; import { UIConfig, UserDisplay } from "@goauthentik/common/ui/config"; import { me } from "@goauthentik/common/users"; import { WebsocketClient } from "@goauthentik/common/ws"; import { AKElement } from "@goauthentik/elements/Base"; import { EnterpriseAwareInterface } from "@goauthentik/elements/Interface"; import "@goauthentik/elements/ak-locale-context"; import "@goauthentik/elements/buttons/ActionButton"; import "@goauthentik/elements/enterprise/EnterpriseStatusBanner"; import "@goauthentik/elements/messages/MessageContainer"; import "@goauthentik/elements/notifications/APIDrawer"; import "@goauthentik/elements/notifications/NotificationDrawer"; import { getURLParam, updateURLParams } from "@goauthentik/elements/router/RouteMatch"; import "@goauthentik/elements/router/RouterOutlet"; import "@goauthentik/elements/sidebar/Sidebar"; import { DefaultBrand } from "@goauthentik/elements/sidebar/SidebarBrand"; import "@goauthentik/elements/sidebar/SidebarItem"; import { themeImage } from "@goauthentik/elements/utils/images"; import { ROUTES } from "@goauthentik/user/Routes"; import "@patternfly/elements/pf-tooltip/pf-tooltip.js"; import { match } from "ts-pattern"; import { msg } from "@lit/localize"; import { css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import PFAvatar from "@patternfly/patternfly/components/Avatar/avatar.css"; import PFBrand from "@patternfly/patternfly/components/Brand/brand.css"; import PFButton from "@patternfly/patternfly/components/Button/button.css"; import PFDrawer from "@patternfly/patternfly/components/Drawer/drawer.css"; import PFDropdown from "@patternfly/patternfly/components/Dropdown/dropdown.css"; import PFNotificationBadge from "@patternfly/patternfly/components/NotificationBadge/notification-badge.css"; import PFPage from "@patternfly/patternfly/components/Page/page.css"; import PFBase from "@patternfly/patternfly/patternfly-base.css"; import PFDisplay from "@patternfly/patternfly/utilities/Display/display.css"; import { CoreApi, CurrentBrand, EventsApi, SessionUser } from "@goauthentik/api"; const customStyles = css` .pf-c-page__main, .pf-c-drawer__content, .pf-c-page__drawer { z-index: auto !important; background-color: transparent !important; } .pf-c-page__header { background-color: transparent !important; box-shadow: none !important; color: black !important; } :host([theme="dark"]) .pf-c-page__header { color: var(--ak-dark-foreground) !important; } :host([theme="light"]) .pf-c-page__header-tools-item .fas, :host([theme="light"]) .pf-c-notification-badge__count, :host([theme="light"]) .pf-c-page__header-tools-group .pf-c-button { color: var(--ak-global--Color--100) !important; } .pf-c-page { background-color: transparent; } .display-none { display: none; } .pf-c-brand { min-height: 32px; height: 32px; } .has-notifications { color: #2b9af3; } .background-wrapper { height: 100vh; width: 100%; position: fixed; z-index: -1; top: 0; left: 0; background-color: var(--pf-c-page--BackgroundColor) !important; } .background-default-slant { background-color: white; /*var(--ak-accent);*/ clip-path: polygon(0 0, 100% 0, 100% 100%, 0 calc(100% - 5vw)); height: 50vh; } :host([theme="dark"]) .background-default-slant { background-color: black; } ak-locale-context { display: flex; flex-direction: column; } .pf-c-drawer__main { min-height: calc(100vh - 76px); max-height: calc(100vh - 76px); } `; // ___ _ _ _ // | _ \_ _ ___ ___ ___ _ _| |_ __ _| |_(_)___ _ _ // | _/ '_/ -_|_-(this.uiConfig.navbar.userDisplay) .with(UserDisplay.username, () => this.me.user.username) .with(UserDisplay.name, () => this.me.user.name) .with(UserDisplay.email, () => this.me.user.email || "") .otherwise(() => this.me.user.username); } get canAccessAdmin() { return ( this.me.user.isSuperuser || // TODO: somehow add `access_admin_interface` to the API schema this.me.user.systemPermissions.includes("access_admin_interface") ); } get isFullyConfigured() { return Boolean(this.uiConfig && this.me && this.brand); } render() { // The `!` in the field definitions above only re-assure typescript and eslint that the // values *should* be available, not that they *are*. Thus this contract check; it asserts // that the contract we promised is being honored, and the rest of the code that depends on // `!` being truthful is not being lied to. if (!this.isFullyConfigured) { throw new Error("ak-interface-user-presentation misused; no valid values passed"); } return html`
${(this.uiConfig.theme.background || "") === "" ? html`
` : html``}
${this.brand.brandingTitle}
${this.renderApiDrawerTrigger()} ${this.renderNotificationDrawerTrigger()} ${this.renderSettings()}
${this.renderAdminInterfaceLink()}
${this.renderImpersonation()}
${this.userDisplayName}
${msg(
`; } renderApiDrawerTrigger() { if (!this.uiConfig.enabledFeatures.apiDrawer) { return nothing; } const onClick = (ev: Event) => { ev.stopPropagation(); this.dispatchEvent( new Event(EVENT_API_DRAWER_TOGGLE, { bubbles: true, composed: true }), ); }; return html`
`; } renderNotificationDrawerTrigger() { if (!this.uiConfig.enabledFeatures.notificationDrawer) { return nothing; } const onClick = (ev: Event) => { ev.stopPropagation(); this.dispatchEvent( new Event(EVENT_NOTIFICATION_DRAWER_TOGGLE, { bubbles: true, composed: true }), ); }; return html`
`; } renderSettings() { if (!this.uiConfig.enabledFeatures.settings) { return nothing; } return html`
`; } renderAdminInterfaceLink() { if (!this.canAccessAdmin) { return nothing; } return html` ${msg("Admin interface")} `; } renderImpersonation() { if (!this.me.original) { return nothing; } const onClick = () => { return new CoreApi(DEFAULT_CONFIG).coreUsersImpersonateEndRetrieve().then(() => { window.location.reload(); }); }; return html` 
${msg("Stop impersonation")}
`; } } // ___ _ // | _ )_ _ __(_)_ _ ___ ______ // | _ \ || (_-< | ' \/ -_|_-<_-< // |___/\_,_/__/_|_||_\___/__/__/ // // @customElement("ak-interface-user") export class UserInterface extends EnterpriseAwareInterface { @property({ type: Boolean }) notificationDrawerOpen = getURLParam("notificationDrawerOpen", false); @state() apiDrawerOpen = getURLParam("apiDrawerOpen", false); ws: WebsocketClient; @state() notificationsCount = 0; @state() me?: SessionUser; constructor() { super(); this.ws = new WebsocketClient(); this.fetchConfigurationDetails(); configureSentry(true); this.toggleNotificationDrawer = this.toggleNotificationDrawer.bind(this); this.toggleApiDrawer = this.toggleApiDrawer.bind(this); this.fetchConfigurationDetails = this.fetchConfigurationDetails.bind(this); } connectedCallback() { super.connectedCallback(); window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, this.toggleNotificationDrawer); window.addEventListener(EVENT_API_DRAWER_TOGGLE, this.toggleApiDrawer); window.addEventListener(EVENT_WS_MESSAGE, this.fetchConfigurationDetails); } disconnectedCallback() { window.removeEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, this.toggleNotificationDrawer); window.removeEventListener(EVENT_API_DRAWER_TOGGLE, this.toggleApiDrawer); window.removeEventListener(EVENT_WS_MESSAGE, this.fetchConfigurationDetails); super.disconnectedCallback(); } toggleNotificationDrawer() { this.notificationDrawerOpen = !this.notificationDrawerOpen; updateURLParams({ notificationDrawerOpen: this.notificationDrawerOpen, }); } toggleApiDrawer() { this.apiDrawerOpen = !this.apiDrawerOpen; updateURLParams({ apiDrawerOpen: this.apiDrawerOpen, }); } fetchConfigurationDetails() { me().then((me: SessionUser) => { this.me = me; new EventsApi(DEFAULT_CONFIG) .eventsNotificationsList({ seen: false, ordering: "-created", pageSize: 1, user: this.me.user.pk, }) .then((notifications) => { this.notificationsCount = notifications.pagination.count; }); }); } get isFullyConfigured() { return Boolean(this.uiConfig && this.me); } render() { if (!this.isFullyConfigured) { return nothing; } return html``; } } declare global { interface HTMLElementTagNameMap { "ak-interface-user-presentation": UserInterfacePresentation; "ak-interface-user": UserInterface; } }