mirror of
https://github.com/jambonz/jambonz-webapp.git
synced 2026-07-24 04:52:05 +00:00
component based on enumScope
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type { Vendor } from "src/vendor/types";
|
||||
import type { Scope } from "src/store/types";
|
||||
|
||||
/** Simple types */
|
||||
|
||||
@@ -116,6 +117,7 @@ export interface User {
|
||||
service_provider_sid?: string | null;
|
||||
initial_password?: string;
|
||||
permissions?: UserPermissions;
|
||||
enumScope: Scope;
|
||||
}
|
||||
|
||||
export interface UserLogin extends User {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import React from "react";
|
||||
import { H1, H2 } from "jambonz-ui";
|
||||
|
||||
import { TestProvider } from "src/test";
|
||||
import { ScopedAccess } from "./scoped-access";
|
||||
|
||||
import type { ScopedAccessProps } from "./scoped-access";
|
||||
|
||||
/** Wrapper to pass different user scopes as enum values */
|
||||
const ScopedAccessTestWrapper = (props: Partial<ScopedAccessProps>) => {
|
||||
return (
|
||||
<TestProvider>
|
||||
<ScopedAccess
|
||||
enumScope={props.enumScope!}
|
||||
noAccessRender={<H2>{"noAccessRender"}</H2>}
|
||||
>
|
||||
<div className="scope-div">
|
||||
<H1>ScopedAccess: {props.enumScope}</H1>
|
||||
</div>
|
||||
</ScopedAccess>
|
||||
</TestProvider>
|
||||
);
|
||||
};
|
||||
|
||||
describe("<ScopedAccess>", () => {
|
||||
it("has sufficient scope - admin", () => {
|
||||
cy.mount(<ScopedAccessTestWrapper enumScope={0} />);
|
||||
cy.get(".scope-div").should("exist");
|
||||
cy.get("h1").should("exist");
|
||||
});
|
||||
|
||||
it("has insufficient scope - service_provider", () => {
|
||||
cy.mount(<ScopedAccessTestWrapper enumScope={1} />);
|
||||
cy.get(".scope-div").should("not.exist");
|
||||
cy.get("h2").should("exist");
|
||||
});
|
||||
|
||||
it("has insufficient scope - account", () => {
|
||||
cy.mount(<ScopedAccessTestWrapper enumScope={2} />);
|
||||
cy.get(".scope-div").should("not.exist");
|
||||
cy.get("h2").should("exist");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import React from "react";
|
||||
|
||||
import { useSelectState } from "src/store";
|
||||
|
||||
import type { Scope } from "src/store/types";
|
||||
|
||||
export type ScopedAccessProps = {
|
||||
enumScope: Scope;
|
||||
children: React.ReactNode;
|
||||
noAccessRender?: React.ReactNode;
|
||||
};
|
||||
|
||||
export const ScopedAccess = ({
|
||||
enumScope,
|
||||
children,
|
||||
noAccessRender,
|
||||
}: ScopedAccessProps) => {
|
||||
const user = useSelectState("user");
|
||||
|
||||
if (user && user.enumScope <= enumScope) {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
if (user && user.enumScope >= enumScope && noAccessRender) {
|
||||
return <>{noAccessRender}</>;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
@@ -16,6 +16,7 @@ import { postServiceProviders } from "src/api";
|
||||
import type { NaviItem } from "./items";
|
||||
|
||||
import "./styles.scss";
|
||||
import { ScopedAccess } from "src/components/scoped-access";
|
||||
|
||||
type CommonProps = {
|
||||
handleMenu: () => void;
|
||||
@@ -149,7 +150,7 @@ export const Navi = ({
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<option> </option>
|
||||
<option>currentServiceProvider.name</option>
|
||||
)}
|
||||
</select>
|
||||
<span>
|
||||
@@ -158,14 +159,16 @@ export const Navi = ({
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setModal(true)}
|
||||
title="Add service provider"
|
||||
className="btnty"
|
||||
>
|
||||
<Icons.PlusCircle />
|
||||
</button>
|
||||
<ScopedAccess enumScope={0}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setModal(true)}
|
||||
title="Add service provider"
|
||||
className="btnty"
|
||||
>
|
||||
<Icons.PlusCircle />
|
||||
</button>
|
||||
</ScopedAccess>
|
||||
</div>
|
||||
{/* Intentionally hiding this as we will need to work this out as we go... */}
|
||||
{/* ACL component will need to be updated for new user scope/permissions handling */}
|
||||
|
||||
@@ -3,7 +3,8 @@ import { sortLocaleName } from "src/utils";
|
||||
import { getToken, parseJwt } from "src/router/auth";
|
||||
|
||||
import type { State, Action } from "./types";
|
||||
import type { ServiceProvider, User } from "src/api/types";
|
||||
import { Scope } from "./types";
|
||||
import { ServiceProvider, User } from "src/api/types";
|
||||
|
||||
/** A generic action assumes action.type is ALWAYS our state key */
|
||||
/** Since this is how we're designing our state interface we cool */
|
||||
@@ -63,7 +64,8 @@ export const currentServiceProviderAction = (
|
||||
|
||||
export const userAsyncAction = async (): Promise<User> => {
|
||||
const token = getToken();
|
||||
return parseJwt(token);
|
||||
const userData = parseJwt(token);
|
||||
return { ...userData, enumScope: Scope[userData.scope] };
|
||||
};
|
||||
|
||||
export const serviceProvidersAsyncAction = async (): Promise<
|
||||
|
||||
@@ -14,6 +14,12 @@ export interface ACL {
|
||||
hasMSTeamsFqdn: boolean;
|
||||
}
|
||||
|
||||
export enum Scope {
|
||||
"admin" = 0,
|
||||
"service_provider" = 1,
|
||||
"account" = 2,
|
||||
}
|
||||
|
||||
export interface FeatureFlag {
|
||||
development: boolean;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user