mirror of
https://github.com/jambonz/jambonz-webapp.git
synced 2026-07-23 20:42:19 +00:00
limitations to carriers and speech
This commit is contained in:
@@ -17,6 +17,8 @@ import type { NaviItem } from "./items";
|
||||
import "./styles.scss";
|
||||
import { ScopedAccess } from "src/components/scoped-access";
|
||||
import { Scope } from "src/store/types";
|
||||
import { USER_ACCOUNT } from "src/api/constants";
|
||||
import { ROUTE_INTERNAL_ACCOUNTS } from "src/router/routes";
|
||||
|
||||
type CommonProps = {
|
||||
handleMenu: () => void;
|
||||
@@ -172,9 +174,47 @@ export const Navi = ({
|
||||
</div>
|
||||
<div className="navi__routes">
|
||||
<ul>
|
||||
{naviTop.map((item) => (
|
||||
<Item key={item.label} item={item} handleMenu={handleMenu} />
|
||||
))}
|
||||
{naviTop.map((item) => {
|
||||
if (item.label === "Settings") {
|
||||
return (
|
||||
<ScopedAccess scope={Scope.service_provider} user={user}>
|
||||
<Item
|
||||
key={item.label}
|
||||
item={item}
|
||||
handleMenu={handleMenu}
|
||||
/>
|
||||
</ScopedAccess>
|
||||
);
|
||||
}
|
||||
if (item.label === "Accounts" && user?.scope === USER_ACCOUNT) {
|
||||
const accountNavItem = {
|
||||
label: "Account",
|
||||
icon: Icons.Activity,
|
||||
route: `${ROUTE_INTERNAL_ACCOUNTS}/${user?.account_sid}/edit`,
|
||||
};
|
||||
return (
|
||||
<Item
|
||||
key={accountNavItem.label}
|
||||
item={accountNavItem}
|
||||
handleMenu={handleMenu}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (item.label === "Accounts") {
|
||||
return (
|
||||
<ScopedAccess scope={Scope.service_provider} user={user}>
|
||||
<Item
|
||||
key={item.label}
|
||||
item={item}
|
||||
handleMenu={handleMenu}
|
||||
/>
|
||||
</ScopedAccess>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Item key={item.label} item={item} handleMenu={handleMenu} />
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="navi__byo">
|
||||
|
||||
@@ -3,8 +3,12 @@ import { ButtonGroup, H1, M, MS } from "jambonz-ui";
|
||||
import dayjs from "dayjs";
|
||||
|
||||
import { getAlerts, useServiceProviderData } from "src/api";
|
||||
import { DATE_SELECTION, PER_PAGE_SELECTION } from "src/api/constants";
|
||||
import { toastError } from "src/store";
|
||||
import {
|
||||
DATE_SELECTION,
|
||||
PER_PAGE_SELECTION,
|
||||
USER_ACCOUNT,
|
||||
} from "src/api/constants";
|
||||
import { toastError, useSelectState } from "src/store";
|
||||
import { hasLength, hasValue } from "src/utils";
|
||||
import {
|
||||
AccountFilter,
|
||||
@@ -18,6 +22,7 @@ import {
|
||||
import type { Account, Alert, PageQuery } from "src/api/types";
|
||||
|
||||
export const Alerts = () => {
|
||||
const user = useSelectState("user");
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
const [accountSid, setAccountSid] = useState("");
|
||||
const [dateFilter, setDateFilter] = useState("today");
|
||||
@@ -69,7 +74,13 @@ export const Alerts = () => {
|
||||
<section className="filters filters--multi">
|
||||
<AccountFilter
|
||||
account={[accountSid, setAccountSid]}
|
||||
accounts={accounts}
|
||||
accounts={
|
||||
user?.scope === USER_ACCOUNT
|
||||
? accounts?.filter(
|
||||
(acct) => acct.account_sid === user.account_sid
|
||||
)
|
||||
: accounts
|
||||
}
|
||||
/>
|
||||
<SelectFilter
|
||||
id="date_filter"
|
||||
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
NETMASK_OPTIONS,
|
||||
TCP_MAX_PORT,
|
||||
TECH_PREFIX_MINLENGTH,
|
||||
USER_ACCOUNT,
|
||||
} from "src/api/constants";
|
||||
import { Icons, Section } from "src/components";
|
||||
import {
|
||||
@@ -63,6 +64,7 @@ export const CarrierForm = ({
|
||||
carrierSmppGateways,
|
||||
}: CarrierFormProps) => {
|
||||
const navigate = useNavigate();
|
||||
const user = useSelectState("user");
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
|
||||
const refSipIp = useRef<HTMLInputElement[]>([]);
|
||||
@@ -437,6 +439,11 @@ export const CarrierForm = ({
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (user?.scope === USER_ACCOUNT && user.account_sid !== accountSid) {
|
||||
toastError("You do not have permissions to make changes to this Carrier");
|
||||
return;
|
||||
}
|
||||
|
||||
setSipMessage("");
|
||||
setSmppInboundMessage("");
|
||||
setSmppOutboundMessage("");
|
||||
@@ -677,11 +684,17 @@ export const CarrierForm = ({
|
||||
<em>Prepend a leading + on origination attempts.</em>
|
||||
</MXS>
|
||||
<AccountSelect
|
||||
accounts={accounts}
|
||||
accounts={
|
||||
user?.scope === USER_ACCOUNT
|
||||
? accounts?.filter(
|
||||
(acct) => user.account_sid === acct.account_sid
|
||||
)
|
||||
: accounts
|
||||
}
|
||||
account={[accountSid, setAccountSid]}
|
||||
label="Used By"
|
||||
required={false}
|
||||
defaultOption
|
||||
defaultOption={user?.scope !== USER_ACCOUNT}
|
||||
/>
|
||||
{accountSid && hasLength(applications) && (
|
||||
<>
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
getFetch,
|
||||
useServiceProviderData,
|
||||
} from "src/api";
|
||||
import { toastSuccess, toastError } from "src/store";
|
||||
import { toastSuccess, toastError, useSelectState } from "src/store";
|
||||
import { ROUTE_INTERNAL_CARRIERS } from "src/router/routes";
|
||||
import {
|
||||
AccountFilter,
|
||||
@@ -18,13 +18,18 @@ import {
|
||||
SearchFilter,
|
||||
} from "src/components";
|
||||
import { hasLength, hasValue, useFilteredResults } from "src/utils";
|
||||
import { API_SIP_GATEWAY, API_SMPP_GATEWAY } from "src/api/constants";
|
||||
import {
|
||||
API_SIP_GATEWAY,
|
||||
API_SMPP_GATEWAY,
|
||||
USER_ACCOUNT,
|
||||
} from "src/api/constants";
|
||||
import { DeleteCarrier } from "./delete";
|
||||
|
||||
import type { Account, Carrier, SipGateway, SmppGateway } from "src/api/types";
|
||||
import { Gateways } from "./gateways";
|
||||
|
||||
export const Carriers = () => {
|
||||
const user = useSelectState("user");
|
||||
const [carrier, setCarrier] = useState<Carrier | null>(null);
|
||||
const [carriers, refetch] = useServiceProviderData<Carrier[]>("VoipCarriers");
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
@@ -48,6 +53,11 @@ export const Carriers = () => {
|
||||
|
||||
const handleDelete = () => {
|
||||
if (carrier) {
|
||||
if (user?.scope === USER_ACCOUNT && user.account_sid !== accountSid) {
|
||||
toastError("You do not have permissions to delete this Carrier");
|
||||
return;
|
||||
}
|
||||
|
||||
deleteCarrier(carrier.voip_carrier_sid)
|
||||
.then(() => {
|
||||
Promise.all([
|
||||
@@ -109,7 +119,13 @@ export const Carriers = () => {
|
||||
/>
|
||||
<AccountFilter
|
||||
account={[accountSid, setAccountSid]}
|
||||
accounts={accounts}
|
||||
accounts={
|
||||
user?.scope === USER_ACCOUNT
|
||||
? accounts?.filter(
|
||||
(acct) => acct.account_sid === user.account_sid
|
||||
)
|
||||
: accounts
|
||||
}
|
||||
label="Used by"
|
||||
defaultOption
|
||||
/>
|
||||
|
||||
@@ -8,12 +8,16 @@ import AdminSettings from "./admin-settings";
|
||||
import type { ServiceProvider } from "src/api/types";
|
||||
import { Section } from "src/components";
|
||||
import { MSG_REQUIRED_FIELDS } from "src/constants";
|
||||
import { ScopedAccess } from "src/components/scoped-access";
|
||||
import { Scope } from "src/store/types";
|
||||
import { useSelectState } from "src/store";
|
||||
|
||||
type SettingsProps = {
|
||||
currentServiceProvider: ServiceProvider;
|
||||
};
|
||||
|
||||
export const Settings = ({ currentServiceProvider }: SettingsProps) => {
|
||||
const user = useSelectState("user");
|
||||
const [activeTab, setActiveTab] = useState("");
|
||||
|
||||
return (
|
||||
@@ -24,14 +28,19 @@ export const Settings = ({ currentServiceProvider }: SettingsProps) => {
|
||||
<fieldset>
|
||||
<MS>{MSG_REQUIRED_FIELDS}</MS>
|
||||
</fieldset>
|
||||
<Tabs active={[activeTab, setActiveTab]}>
|
||||
<Tab id="admin" label="Admin">
|
||||
<AdminSettings />
|
||||
</Tab>
|
||||
<Tab id="serviceProvider" label="Service Provider">
|
||||
<ServiceProviderSettings />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
<ScopedAccess scope={Scope.admin} user={user}>
|
||||
<Tabs active={[activeTab, setActiveTab]}>
|
||||
<Tab id="admin" label="Admin">
|
||||
<AdminSettings />
|
||||
</Tab>
|
||||
<Tab id="serviceProvider" label="Service Provider">
|
||||
<ServiceProviderSettings />
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</ScopedAccess>
|
||||
<ScopedAccess scope={Scope.service_provider} user={user}>
|
||||
<ServiceProviderSettings />
|
||||
</ScopedAccess>
|
||||
</form>
|
||||
</Section>
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ import { CredentialStatus } from "./status";
|
||||
|
||||
import type { RegionVendors, GoogleServiceKey, Vendor } from "src/vendor/types";
|
||||
import type { Account, SpeechCredential, UseApiDataMap } from "src/api/types";
|
||||
import { USER_ACCOUNT } from "src/api/constants";
|
||||
|
||||
type SpeechServiceFormProps = {
|
||||
credential?: UseApiDataMap<SpeechCredential>;
|
||||
@@ -40,6 +41,7 @@ type SpeechServiceFormProps = {
|
||||
|
||||
export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
const navigate = useNavigate();
|
||||
const user = useSelectState("user");
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
const regions = useRegionVendors();
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
@@ -114,6 +116,12 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
};
|
||||
|
||||
if (credential && credential.data) {
|
||||
if (user?.scope === USER_ACCOUNT && user.account_sid !== accountSid) {
|
||||
toastError(
|
||||
"You do not have permissions to make changes to these Speech Credentials"
|
||||
);
|
||||
return;
|
||||
}
|
||||
/** The backend API returns obscured secrets now so we need to make sure we don't send them back */
|
||||
/** Fields not sent back via :PUT are `service_key`, `access_key_id`, `secret_access_key` and `api_key` */
|
||||
putSpeechService(
|
||||
@@ -258,10 +266,16 @@ export const SpeechServiceForm = ({ credential }: SpeechServiceFormProps) => {
|
||||
</fieldset>
|
||||
<fieldset>
|
||||
<AccountSelect
|
||||
accounts={accounts}
|
||||
accounts={
|
||||
user?.scope === USER_ACCOUNT
|
||||
? accounts?.filter(
|
||||
(acct) => acct.account_sid === user.account_sid
|
||||
)
|
||||
: accounts
|
||||
}
|
||||
account={[accountSid, setAccountSid]}
|
||||
required={false}
|
||||
defaultOption
|
||||
defaultOption={user?.scope !== USER_ACCOUNT}
|
||||
disabled={credential ? true : false}
|
||||
/>
|
||||
</fieldset>
|
||||
|
||||
@@ -2,7 +2,11 @@ import React, { useEffect, useState } from "react";
|
||||
import { Button, H1, Icon, M } from "jambonz-ui";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
import { API_ACCOUNTS, API_SERVICE_PROVIDERS } from "src/api/constants";
|
||||
import {
|
||||
API_ACCOUNTS,
|
||||
API_SERVICE_PROVIDERS,
|
||||
USER_ACCOUNT,
|
||||
} from "src/api/constants";
|
||||
import { AccountFilter, Icons, Section, Spinner } from "src/components";
|
||||
import { useSelectState, toastError, toastSuccess } from "src/store";
|
||||
import { getFetch, deleteSpeechService, useServiceProviderData } from "src/api";
|
||||
@@ -15,6 +19,7 @@ import { CredentialStatus } from "./status";
|
||||
import type { SpeechCredential, Account } from "src/api/types";
|
||||
|
||||
export const SpeechServices = () => {
|
||||
const user = useSelectState("user");
|
||||
const currentServiceProvider = useSelectState("currentServiceProvider");
|
||||
const [accounts] = useServiceProviderData<Account[]>("Accounts");
|
||||
const [accountSid, setAccountSid] = useState("");
|
||||
@@ -33,6 +38,12 @@ export const SpeechServices = () => {
|
||||
|
||||
const handleDelete = () => {
|
||||
if (credential && currentServiceProvider) {
|
||||
if (user?.scope === USER_ACCOUNT && user.account_sid !== accountSid) {
|
||||
toastError(
|
||||
"You do not have permissions to delete these Speech Credentials"
|
||||
);
|
||||
return;
|
||||
}
|
||||
deleteSpeechService(
|
||||
currentServiceProvider.service_provider_sid,
|
||||
credential.speech_credential_sid
|
||||
|
||||
@@ -6,7 +6,11 @@ import { isValidPasswd } from "src/utils";
|
||||
import { putUser, useApiData } from "src/api";
|
||||
import { PasswordSettings, StatusCodes } from "src/api/types";
|
||||
import { Passwd, Message } from "src/components/forms";
|
||||
import { ROUTE_LOGIN, ROUTE_INTERNAL_ACCOUNTS } from "src/router/routes";
|
||||
import {
|
||||
ROUTE_LOGIN,
|
||||
ROUTE_INTERNAL_ACCOUNTS,
|
||||
ROUTE_INTERNAL_APPLICATIONS,
|
||||
} from "src/router/routes";
|
||||
import {
|
||||
SESS_OLD_PASSWORD,
|
||||
SESS_USER_SID,
|
||||
@@ -17,8 +21,11 @@ import {
|
||||
} from "src/constants";
|
||||
|
||||
import type { IMessage } from "src/store/types";
|
||||
import { useSelectState } from "src/store";
|
||||
import { USER_ACCOUNT } from "src/api/constants";
|
||||
|
||||
export const CreatePassword = () => {
|
||||
const user = useSelectState("user");
|
||||
const [passwdSettings] = useApiData<PasswordSettings>("PasswordSettings");
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
@@ -65,7 +72,11 @@ export const CreatePassword = () => {
|
||||
if (response.status === StatusCodes.NO_CONTENT) {
|
||||
sessionStorage.clear();
|
||||
|
||||
navigate(ROUTE_INTERNAL_ACCOUNTS);
|
||||
navigate(
|
||||
user?.scope !== USER_ACCOUNT
|
||||
? ROUTE_INTERNAL_ACCOUNTS
|
||||
: ROUTE_INTERNAL_APPLICATIONS
|
||||
);
|
||||
} else {
|
||||
setMessage(MSG_SOMETHING_WRONG);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user