limitations to carriers and speech

This commit is contained in:
eglehelms
2022-11-30 12:22:23 +01:00
parent ce4c5bfb68
commit 92afbfb388
8 changed files with 149 additions and 24 deletions
+43 -3
View File
@@ -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">
+14 -3
View File
@@ -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
+13 -2
View File
@@ -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);
}