Introducer swagger typescript api (#2704)

Signed-off-by: Daniel Valdivia <18384552+dvaldivia@users.noreply.github.com>
This commit is contained in:
Daniel Valdivia
2023-03-17 08:57:45 -07:00
committed by GitHub
parent ad0591ec17
commit 8b1b2b1e2d
21 changed files with 5531 additions and 377 deletions

View File

@@ -30,4 +30,6 @@ tests/
.vscode/
*.code-workspace
*~
.eslintcache
.eslintcache
consoleApi.ts

View File

@@ -53,6 +53,8 @@ clean-swagger:
swagger-console:
@echo "Generating swagger server code from yaml"
@swagger generate server -A console --main-package=management --server-package=restapi --exclude-main -P models.Principal -f ./swagger.yml -r NOTICE
@echo "Generating typescript api"
@npx swagger-typescript-api -p ./swagger.yml -o ./portal-ui/src/api -n consoleApi.ts
assets:

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,35 @@
// This file is part of MinIO Console Server
// Copyright (c) 2023 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { ErrorResponseHandler } from "../common/types";
import { Error } from "./consoleApi";
// errorToHandler translates a swagger error to a ErrorResponseHandler which
// is legacy, when all API calls are using the swagger API, we can remove this.
export const errorToHandler = (e: Error): ErrorResponseHandler => {
if (!e) {
return {
statusCode: 0,
errorMessage: "",
detailedError: "",
};
}
return {
statusCode: e.code,
errorMessage: e.message,
detailedError: e.detailedMessage,
};
};

View File

@@ -0,0 +1,38 @@
import { Api, HttpResponse, Error, FullRequestParams } from "./consoleApi";
export let api = new Api();
const internalRequestFunc = api.request;
api.request = async <T = any, E = any>({
body,
secure,
path,
type,
query,
format,
baseUrl,
cancelToken,
...params
}: FullRequestParams): Promise<HttpResponse<T, E>> => {
const internalResp = internalRequestFunc({
body,
secure,
path,
type,
query,
format,
baseUrl,
cancelToken,
...params,
});
return internalResp.then(CommonAPIValidation);
};
export function CommonAPIValidation<D, E>(
res: HttpResponse<D, E>
): HttpResponse<D, E> {
const err = res.error as Error;
if (err && err.code === 403 && err.message === "invalid session") {
document.location = "/";
}
return res;
}

View File

@@ -22,7 +22,6 @@ import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { TabPanel } from "../../../shared/tabs";
import { Policy } from "../../Policies/types";
import { User } from "../../Users/types";
import { ErrorResponseHandler } from "../../../../common/types";
import TableWrapper from "../../Common/TableWrapper/TableWrapper";
@@ -41,6 +40,7 @@ import { encodeURLString } from "../../../../common/utils";
import { setErrorSnackMessage } from "../../../../systemSlice";
import { selBucketDetailsLoading } from "./bucketDetailsSlice";
import { useAppDispatch } from "../../../../store";
import { Policy } from "../../../../api/consoleApi";
function a11yProps(index: any) {
return {

View File

@@ -20,9 +20,10 @@ import { LinearProgress } from "@mui/material";
import { Theme } from "@mui/material/styles";
import { useNavigate } from "react-router-dom";
import { BackLink, BucketsIcon, Button, HelpBox, InfoIcon } from "mds";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { containerForHeader } from "../../../Common/FormComponents/common/styleLibrary";
import {
containerForHeader,
modalBasic,
} from "../../../Common/FormComponents/common/styleLibrary";
import InputBoxWrapper from "../../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
import RadioGroupSelector from "../../../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
import { k8sScalarUnitsExcluding } from "../../../../../common/utils";
@@ -34,9 +35,6 @@ import {
selSiteRep,
setErrorSnackMessage,
} from "../../../../../systemSlice";
import { ErrorResponseHandler } from "../../../../../common/types";
import { BucketList } from "../../types";
import api from "../../../../../common/api";
import PageLayout from "../../../Common/Layout/PageLayout";
import InputUnitMenu from "../../../Common/FormComponents/InputUnitMenu/InputUnitMenu";
import FormLayout from "../../../Common/FormLayout";
@@ -65,52 +63,57 @@ import {
import { hasPermission } from "../../../../../common/SecureComponent";
import BucketNamingRules from "./BucketNamingRules";
import PageHeaderWrapper from "../../../Common/PageHeaderWrapper/PageHeaderWrapper";
import { api } from "../../../../../api";
import {
Error,
HttpResponse,
ListBucketsResponse,
} from "../../../../../api/consoleApi";
import { errorToHandler } from "../../../../../api/errors";
import makeStyles from "@mui/styles/makeStyles";
const styles = (theme: Theme) =>
createStyles({
buttonContainer: {
marginTop: 24,
display: "flex",
justifyContent: "flex-end",
"& button": {
marginLeft: 8,
},
const useStyles = makeStyles((theme: Theme) => ({
buttonContainer: {
marginTop: 24,
display: "flex",
justifyContent: "flex-end",
"& button": {
marginLeft: 8,
},
error: {
color: "#b53b4b",
border: "1px solid #b53b4b",
padding: 8,
borderRadius: 3,
},
error: {
color: "#b53b4b",
border: "1px solid #b53b4b",
padding: 8,
borderRadius: 3,
},
alertVersioning: {
border: "#E2E2E2 1px solid",
backgroundColor: "#FBFAFA",
borderRadius: 3,
display: "flex",
alignItems: "center",
padding: "10px",
color: "#767676",
"& > .min-icon ": {
width: 20,
height: 20,
marginRight: 10,
},
alertVersioning: {
border: "#E2E2E2 1px solid",
backgroundColor: "#FBFAFA",
borderRadius: 3,
display: "flex",
alignItems: "center",
padding: "10px",
color: "#767676",
"& > .min-icon ": {
width: 20,
height: 20,
marginRight: 10,
},
},
h6title: {
fontWeight: "bold",
color: "#000000",
fontSize: 20,
},
...containerForHeader,
});
},
h6title: {
fontWeight: "bold",
color: "#000000",
fontSize: 20,
},
...containerForHeader,
...modalBasic,
}));
interface IsetProps {
classes: any;
}
const AddBucket = ({ classes }: IsetProps) => {
const AddBucket = () => {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const classes = useStyles();
const validBucketCharacters = new RegExp(
`^[a-z0-9][a-z0-9\\.\\-]{1,61}[a-z0-9]$`
@@ -195,19 +198,23 @@ const AddBucket = ({ classes }: IsetProps) => {
dispatch(setName(""));
dispatch(setIsDirty(false));
const fetchRecords = () => {
api
.invoke("GET", `/api/v1/buckets`)
.then((res: BucketList) => {
var bucketList: string[] = [];
if (res.buckets != null && res.buckets.length > 0) {
res.buckets.forEach((bucket) => {
bucketList.push(bucket.name);
});
api.buckets
.listBuckets()
.then((res: HttpResponse<ListBucketsResponse, Error>) => {
if (res.data) {
var bucketList: string[] = [];
if (res.data.buckets != null && res.data.buckets.length > 0) {
res.data.buckets.forEach((bucket) => {
bucketList.push(bucket.name);
});
}
setRecords(bucketList);
} else if (res.error) {
dispatch(setErrorSnackMessage(errorToHandler(res.error)));
}
setRecords(bucketList);
})
.catch((err: ErrorResponseHandler) => {
dispatch(setErrorSnackMessage(err));
.catch((err) => {
dispatch(setErrorSnackMessage(errorToHandler(err)));
});
};
fetchRecords();
@@ -544,4 +551,4 @@ const AddBucket = ({ classes }: IsetProps) => {
);
};
export default withStyles(styles)(AddBucket);
export default AddBucket;

View File

@@ -14,14 +14,15 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { MakeBucketRequest } from "../../types";
import { getBytes } from "../../../../../common/utils";
import api from "../../../../../common/api";
import { ErrorResponseHandler } from "../../../../../common/types";
import { setErrorSnackMessage } from "../../../../../systemSlice";
import { createAsyncThunk } from "@reduxjs/toolkit";
import { AppState } from "../../../../../store";
import { resetForm } from "./addBucketsSlice";
import { api } from "../../../../../api";
import {
MakeBucketRequest,
ObjectRetentionMode,
ObjectRetentionUnit,
} from "../../../../../api/consoleApi";
export const addBucketAsync = createAsyncThunk(
"buckets/addBucketAsync",
@@ -62,23 +63,13 @@ export const addBucketAsync = createAsyncThunk(
if (retentionEnabled) {
request.retention = {
mode: retentionMode,
unit: retentionUnit,
mode: retentionMode as ObjectRetentionMode,
unit: retentionUnit as ObjectRetentionUnit,
validity: retentionValidity,
};
}
}
return api
.invoke("POST", "/api/v1/buckets", request)
.then((res) => {
const newBucketName = `${bucketName}`;
dispatch(resetForm());
return newBucketName;
})
.catch((err: ErrorResponseHandler) => {
dispatch(setErrorSnackMessage(err));
return rejectWithValue(err);
});
return api.buckets.makeBucket(request);
}
);

View File

@@ -18,7 +18,6 @@ import get from "lodash/get";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import { BucketsIcon, ReportedUsageIcon, TotalObjectsIcon } from "mds";
import { Bucket } from "../types";
import { Box, Grid, Typography } from "@mui/material";
import {
calculateBytes,
@@ -34,6 +33,7 @@ import {
import { hasPermission } from "../../../../common/SecureComponent";
import clsx from "clsx";
import makeStyles from "@mui/styles/makeStyles";
import { Bucket } from "../../../../api/consoleApi";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@@ -248,7 +248,10 @@ const BucketListItem = ({
<Grid container className={classes.bucketInfo}>
<Grid item xs={12} sm paddingRight={5}>
<Typography variant="body2">
Created: {new Date(bucket.creation_date).toString()}
Created:{" "}
{bucket.creation_date
? new Date(bucket.creation_date).toString()
: "n/a"}
</Typography>
</Grid>
<Grid item xs={12} sm>

View File

@@ -29,17 +29,14 @@ import {
SelectAllIcon,
SelectMultipleIcon,
} from "mds";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { LinearProgress } from "@mui/material";
import Grid from "@mui/material/Grid";
import { Bucket, BucketList } from "../types";
import {
actionsTray,
containerForHeader,
searchField,
} from "../../Common/FormComponents/common/styleLibrary";
import { ErrorResponseHandler } from "../../../../common/types";
import api from "../../../../common/api";
import BucketListItem from "./BucketListItem";
import BulkReplicationModal from "./BulkReplicationModal";
import { SecureComponent } from "../../../../common/SecureComponent";
@@ -65,33 +62,39 @@ import TooltipWrapper from "../../Common/TooltipWrapper/TooltipWrapper";
import AButton from "../../Common/AButton/AButton";
import { setLoadingObjects } from "../../ObjectBrowser/objectBrowserSlice";
import PageHeaderWrapper from "../../Common/PageHeaderWrapper/PageHeaderWrapper";
import makeStyles from "@mui/styles/makeStyles";
import { api } from "../../../../api";
import {
Bucket,
Error,
HttpResponse,
ListBucketsResponse,
} from "../../../../api/consoleApi";
import { errorToHandler } from "../../../../api/errors";
const styles = (theme: Theme) =>
createStyles({
bucketList: {
marginTop: 25,
height: "calc(100vh - 211px)",
"&.isEmbedded": {
height: "calc(100vh - 128px)",
},
const useStyles = makeStyles((theme: Theme) => ({
bucketList: {
marginTop: 25,
height: "calc(100vh - 211px)",
"&.isEmbedded": {
height: "calc(100vh - 128px)",
},
searchField: {
...searchField.searchField,
minWidth: 380,
"@media (max-width: 900px)": {
minWidth: 220,
},
},
searchField: {
...searchField.searchField,
minWidth: 380,
"@media (max-width: 900px)": {
minWidth: 220,
},
...containerForHeader,
});
},
...containerForHeader,
...actionsTray,
}));
interface IListBucketsProps {
classes: any;
}
const ListBuckets = ({ classes }: IListBucketsProps) => {
const ListBuckets = () => {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const classes = useStyles();
const [records, setRecords] = useState<Bucket[]>([]);
const [loading, setLoading] = useState<boolean>(true);
@@ -110,16 +113,17 @@ const ListBuckets = ({ classes }: IListBucketsProps) => {
if (loading) {
const fetchRecords = () => {
setLoading(true);
api
.invoke("GET", `/api/v1/buckets`)
.then((res: BucketList) => {
setLoading(false);
setRecords(res.buckets || []);
dispatch(setLoadingObjects(true));
})
.catch((err: ErrorResponseHandler) => {
setLoading(false);
dispatch(setErrorSnackMessage(err));
api.buckets
.listBuckets()
.then((res: HttpResponse<ListBucketsResponse, Error>) => {
if (res.data) {
setLoading(false);
setRecords(res.data.buckets || []);
dispatch(setLoadingObjects(true));
} else if (res.error) {
setLoading(false);
dispatch(setErrorSnackMessage(errorToHandler(res.error)));
}
});
};
fetchRecords();
@@ -483,4 +487,4 @@ const ListBuckets = ({ classes }: IListBucketsProps) => {
);
};
export default withStyles(styles)(ListBuckets);
export default ListBuckets;

View File

@@ -14,21 +14,6 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
export interface RwAccess {
write: boolean;
read: boolean;
}
export interface Bucket {
name: string;
creation_date: string;
size?: number;
objects?: number;
rw_access?: RwAccess;
manage?: boolean;
details?: Details;
}
export interface BucketEncryptionInfo {
algorithm: string;
kmsMasterKeyID: string;
@@ -50,11 +35,6 @@ export interface BucketInfo {
size?: number;
}
export interface BucketList {
buckets: Bucket[];
total: number;
}
export interface BucketEvent {
id: string;
arn: string;
@@ -68,11 +48,6 @@ export interface BucketEventList {
total: number;
}
export interface BucketPolicy {
name: string;
body: string;
}
export interface ArnList {
arns: string[];
}
@@ -92,10 +67,6 @@ export interface BucketObjectLocking {
object_locking_enabled: boolean;
}
export interface BucketReplicationRuleDeleteMarker {
status: string;
}
export interface BucketReplicationDestination {
bucket: string;
}
@@ -124,26 +95,6 @@ export interface BucketQuota {
type: string;
}
export interface QuotaRequest {
enabled: boolean;
quota_type: string;
amount: number;
}
export interface RetentionRequest {
mode: string;
unit: string;
validity: number;
}
export interface MakeBucketRequest {
name: string;
versioning: boolean;
locking: boolean;
quota?: QuotaRequest;
retention?: RetentionRequest;
}
export interface ChangePasswordRequest {
current_secret_key: string;
new_secret_key: string;
@@ -154,27 +105,6 @@ export interface ChangeUserPasswordRequest {
newSecretKey: string;
}
export interface IRemoteBucket {
name: string;
accessKey: string;
secretKey: string;
sourceBucket: string;
targetURL: string;
targetBucket: string;
remoteARN: string;
status: string;
service: string;
}
export interface PermissionAction {
id: string;
can: boolean;
}
export interface HasPermissionResponse {
permissions: PermissionAction[];
}
export interface BulkReplicationResponse {
replicationState: BulkReplicationItem[];
}

View File

@@ -36,9 +36,14 @@ import { routesAsKbarActions } from "./kbar-actions";
import { Box } from "@mui/material";
import { MenuExpandedIcon } from "mds";
import { useSelector } from "react-redux";
import useApi from "./Common/Hooks/useApi";
import { Bucket, BucketList } from "./Buckets/types";
import { selFeatures } from "./consoleSlice";
import {
Bucket,
Error,
HttpResponse,
ListBucketsResponse,
} from "../../api/consoleApi";
import { api } from "../../api";
const useStyles = makeStyles((theme: Theme) => ({
resultItem: {
@@ -131,15 +136,18 @@ const CommandBar = () => {
const [buckets, setBuckets] = useState<Bucket[]>([]);
const [, invokeListBucketsApi] = useApi(
(res: BucketList) => {
setBuckets(res.buckets);
},
() => {}
);
const invokeListBucketsApi = () => {
api.buckets
.listBuckets()
.then((res: HttpResponse<ListBucketsResponse, Error>) => {
if (res.data !== undefined) {
setBuckets(res.data.buckets || []);
}
});
};
const fetchBuckets = useCallback(() => {
invokeListBucketsApi("GET", `/api/v1/buckets`);
invokeListBucketsApi();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

View File

@@ -22,14 +22,11 @@ import { BucketsIcon, Button, HelpBox, RefreshIcon } from "mds";
import createStyles from "@mui/styles/createStyles";
import { LinearProgress } from "@mui/material";
import Grid from "@mui/material/Grid";
import { Bucket, BucketList } from "../Buckets/types";
import {
actionsTray,
containerForHeader,
searchField,
} from "../Common/FormComponents/common/styleLibrary";
import { ErrorResponseHandler } from "../../../common/types";
import api from "../../../common/api";
import { SecureComponent } from "../../../common/SecureComponent";
import {
@@ -48,11 +45,19 @@ import { selFeatures } from "../consoleSlice";
import AutoColorIcon from "../Common/Components/AutoColorIcon";
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
import AButton from "../Common/AButton/AButton";
import { setLoadingObjects } from "../ObjectBrowser/objectBrowserSlice";
import makeStyles from "@mui/styles/makeStyles";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import { niceBytesInt } from "../../../common/utils";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import {
Bucket,
HttpResponse,
ListBucketsResponse,
Error,
} from "../../../api/consoleApi";
import { api } from "../../../api";
import { errorToHandler } from "../../../api/errors";
import { setLoadingObjects } from "./objectBrowserSlice";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@@ -91,16 +96,18 @@ const OBListBuckets = () => {
if (loading) {
const fetchRecords = () => {
setLoading(true);
api
.invoke("GET", `/api/v1/buckets`)
.then((res: BucketList) => {
setLoading(false);
setRecords(res.buckets || []);
dispatch(setLoadingObjects(true));
api.buckets
.listBuckets()
.then((res: HttpResponse<ListBucketsResponse, Error>) => {
if (res.data) {
setLoading(false);
setRecords(res.data.buckets || []);
dispatch(setLoadingObjects(true));
}
})
.catch((err: ErrorResponseHandler) => {
.catch((err) => {
setLoading(false);
dispatch(setErrorSnackMessage(err));
dispatch(setErrorSnackMessage(errorToHandler(err)));
});
};
fetchRecords();

View File

@@ -24,13 +24,14 @@ import AddPolicyHelpBox from "./AddPolicyHelpBox";
import CodeMirrorWrapper from "../Common/FormComponents/CodeMirrorWrapper/CodeMirrorWrapper";
import { IAM_PAGES } from "../../../common/SecureComponent/permissions";
import { ErrorResponseHandler } from "../../../common/types";
import api from "../../../../src/common/api";
import FormLayout from "../Common/FormLayout";
import { setErrorSnackMessage } from "../../../systemSlice";
import { useNavigate } from "react-router-dom";
import { useAppDispatch } from "../../../store";
import { emptyPolicy } from "./utils";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import { api } from "../../../api";
import { Error, HttpResponse, Policy } from "../../../api/consoleApi";
const AddPolicyScreen = () => {
const dispatch = useAppDispatch();
@@ -46,12 +47,12 @@ const AddPolicyScreen = () => {
return;
}
setAddLoading(true);
api
.invoke("POST", "/api/v1/policies", {
api.policies
.addPolicy({
name: policyName,
policy: policyDefinition,
})
.then((res) => {
.then((res: HttpResponse<Policy, Error>) => {
setAddLoading(false);
navigate(`${IAM_PAGES.POLICIES}`);
})

View File

@@ -17,12 +17,10 @@
import React, { Fragment, useEffect, useState } from "react";
import { AddIcon, Button, HelpBox, IAMPoliciesIcon } from "mds";
import { useNavigate } from "react-router-dom";
import get from "lodash/get";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import Grid from "@mui/material/Grid";
import { Policy, PolicyList } from "./types";
import {
actionsTray,
@@ -32,7 +30,6 @@ import {
import { ErrorResponseHandler } from "../../../common/types";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import api from "../../../common/api";
import PageLayout from "../Common/Layout/PageLayout";
import {
CONSOLE_UI_RESOURCE,
@@ -57,6 +54,13 @@ import { setErrorSnackMessage } from "../../../systemSlice";
import { useAppDispatch } from "../../../store";
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import { api } from "../../../api";
import {
Error,
HttpResponse,
ListPoliciesResponse,
Policy,
} from "../../../api/consoleApi";
const DeletePolicy = withSuspense(React.lazy(() => import("./DeletePolicy")));
@@ -115,17 +119,17 @@ const ListPolicies = ({ classes }: IPoliciesProps) => {
useEffect(() => {
if (loading) {
if (canDisplayPolicies) {
api
.invoke("GET", `/api/v1/policies`)
.then((res: PolicyList) => {
const policies = get(res, "policies", []);
api.policies
.listPolicies()
.then((res: HttpResponse<ListPoliciesResponse, Error>) => {
const policies = res.data.policies ?? [];
policies.sort((pa, pb) => {
if (pa.name > pb.name) {
if (pa.name! > pb.name!) {
return 1;
}
if (pa.name < pb.name) {
if (pa.name! < pb.name!) {
return -1;
}
@@ -181,7 +185,7 @@ const ListPolicies = ({ classes }: IPoliciesProps) => {
];
const filteredRecords = records.filter((elementItem) =>
elementItem.name.includes(filterPolicies)
elementItem.name?.includes(filterPolicies)
);
return (

View File

@@ -14,7 +14,6 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { IAMPolicy, IAMStatement, Policy } from "./types";
import { useSelector } from "react-redux";
import { useNavigate, useParams } from "react-router-dom";
import {
@@ -38,7 +37,6 @@ import Paper from "@mui/material/Paper";
import Grid from "@mui/material/Grid";
import { LinearProgress } from "@mui/material";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import api from "../../../common/api";
import { ErrorResponseHandler } from "../../../common/types";
import CodeMirrorWrapper from "../Common/FormComponents/CodeMirrorWrapper/CodeMirrorWrapper";
@@ -75,6 +73,14 @@ import { selFeatures } from "../consoleSlice";
import { useAppDispatch } from "../../../store";
import TooltipWrapper from "../Common/TooltipWrapper/TooltipWrapper";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import {
Error,
HttpResponse,
Policy,
ServiceAccounts,
} from "../../../api/consoleApi";
import { api } from "../../../api";
import { IAMPolicy, IAMStatement } from "./types";
const DeletePolicy = withSuspense(React.lazy(() => import("./DeletePolicy")));
@@ -180,8 +186,8 @@ const PolicyDetails = ({ classes }: IPolicyDetailsProps) => {
}
setAddLoading(true);
if (canEditPolicy) {
api
.invoke("POST", "/api/v1/policies", {
api.policies
.addPolicy({
name: policyName,
policy: policyDefinition,
})
@@ -203,13 +209,10 @@ const PolicyDetails = ({ classes }: IPolicyDetailsProps) => {
const loadUsersForPolicy = () => {
if (loadingUsers) {
if (displayUsers && !ldapIsEnabled) {
api
.invoke(
"GET",
`/api/v1/policies/${encodeURLString(policyName)}/users`
)
.then((result: any) => {
setUserList(result);
api.policies
.listUsersForPolicy(encodeURLString(policyName))
.then((result: HttpResponse<ServiceAccounts, Error>) => {
setUserList(result.data ?? []);
setLoadingUsers(false);
})
.catch((err: ErrorResponseHandler) => {
@@ -225,13 +228,10 @@ const PolicyDetails = ({ classes }: IPolicyDetailsProps) => {
const loadGroupsForPolicy = () => {
if (loadingGroups) {
if (displayGroups && !ldapIsEnabled) {
api
.invoke(
"GET",
`/api/v1/policies/${encodeURLString(policyName)}/groups`
)
.then((result: any) => {
setGroupList(result);
api.policies
.listGroupsForPolicy(encodeURLString(policyName))
.then((result: HttpResponse<ServiceAccounts, Error>) => {
setGroupList(result.data ?? []);
setLoadingGroups(false);
})
.catch((err: ErrorResponseHandler) => {
@@ -246,17 +246,17 @@ const PolicyDetails = ({ classes }: IPolicyDetailsProps) => {
const loadPolicyDetails = () => {
if (loadingPolicy) {
if (displayPolicy) {
api
.invoke("GET", `/api/v1/policy/${encodeURLString(policyName)}`)
.then((result: any) => {
if (result) {
setPolicy(result);
api.policy
.policyInfo(encodeURLString(policyName))
.then((result: HttpResponse<Policy, Error>) => {
if (result.data) {
setPolicy(result.data);
setPolicyDefinition(
result
? JSON.stringify(JSON.parse(result.policy), null, 4)
? JSON.stringify(JSON.parse(result.data?.policy!), null, 4)
: ""
);
const pol: IAMPolicy = JSON.parse(result.policy);
const pol: IAMPolicy = JSON.parse(result.data?.policy!);
setPolicyStatements(pol.Statement);
}
setLoadingPolicy(false);

View File

@@ -28,16 +28,20 @@ import {
selectorsCommon,
tableStyles,
} from "../Common/FormComponents/common/styleLibrary";
import { PolicyList } from "./types";
import { ErrorResponseHandler } from "../../../common/types";
import api from "../../../common/api";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import SearchBox from "../Common/SearchBox";
import { setModalErrorSnackMessage } from "../../../systemSlice";
import { AppState, useAppDispatch } from "../../../store";
import { setSelectedPolicies } from "../Users/AddUsersSlice";
import { useSelector } from "react-redux";
import { api } from "../../../api";
import {
HttpResponse,
ListPoliciesResponse,
Error,
} from "../../../api/consoleApi";
interface ISelectPolicyProps {
classes: any;
@@ -90,10 +94,10 @@ const PolicySelectors = ({ classes, noTitle = false }: ISelectPolicyProps) => {
const fetchPolicies = useCallback(() => {
isLoading(true);
api
.invoke("GET", `/api/v1/policies?limit=1000`)
.then((res: PolicyList) => {
const policies = res.policies === null ? [] : res.policies;
api.policies
.listPolicies()
.then((res: HttpResponse<ListPoliciesResponse, Error>) => {
const policies = res.data.policies ?? [];
isLoading(false);
setRecords(policies.sort(policySort));
})

View File

@@ -13,25 +13,23 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useState } from "react";
import { IAMStatement } from "./types";
import React, { Fragment, useState } from "react";
import { Box } from "@mui/material";
import Grid from "@mui/material/Grid";
import SearchBox from "../Common/SearchBox";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import { searchField } from "../Common/FormComponents/common/styleLibrary";
import withStyles from "@mui/styles/withStyles";
import { DisabledIcon, EnabledIcon } from "mds";
import { STATUS_COLORS } from "../Dashboard/BasicDashboard/Utils";
import makeStyles from "@mui/styles/makeStyles";
import { IAMStatement } from "./types";
const styles = (theme: Theme) =>
createStyles({
searchField: {
...searchField.searchField,
maxWidth: 380,
},
});
const useStyles = makeStyles((theme: Theme) => ({
searchField: {
...searchField.searchField,
maxWidth: 380,
},
}));
const rowGridStyle = {
display: "grid",
@@ -57,11 +55,11 @@ const Highlight = ({ search = "", children = "" }): any => {
const PolicyView = ({
policyStatements,
classes = {},
}: {
policyStatements: IAMStatement[];
classes?: any;
}) => {
const classes = useStyles();
const [filter, setFilter] = useState<string>("");
return (
@@ -88,104 +86,107 @@ const PolicyView = ({
/>
</Box>
</Grid>
<Grid
item
xs={12}
sx={{
"& .policy-row": {
borderBottom: "1px solid #eaeaea",
},
"& .policy-row:first-child": {
borderTop: "1px solid #eaeaea",
},
"& .policy-row:last-child": {
borderBottom: "0px",
},
paddingTop: "15px",
"& mark": {
color: "#000000",
fontWeight: 500,
},
}}
>
{policyStatements.map((stmt, i) => {
const effect = stmt.Effect;
const isAllow = effect === "Allow";
return (
<Box
className="policy-row"
key={`${i}`}
sx={{
display: "grid",
gridTemplateColumns: "1fr",
gap: "15px",
fontSize: "14px",
padding: "10px 0 10px 0",
"& .label": {
fontWeight: 600,
},
}}
>
<Box sx={rowGridStyle}>
<Box className="label">Effect:</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
"& .min-icon": {
marginRight: "5px",
fill: isAllow ? STATUS_COLORS.GREEN : STATUS_COLORS.RED,
height: "14px",
width: "14px",
},
}}
>
{isAllow ? <EnabledIcon /> : <DisabledIcon />}
{effect}
</Box>
</Box>
{!policyStatements && <Fragment>Policy has no statements</Fragment>}
{policyStatements && (
<Grid
item
xs={12}
sx={{
"& .policy-row": {
borderBottom: "1px solid #eaeaea",
},
"& .policy-row:first-child": {
borderTop: "1px solid #eaeaea",
},
"& .policy-row:last-child": {
borderBottom: "0px",
},
paddingTop: "15px",
"& mark": {
color: "#000000",
fontWeight: 500,
},
}}
>
{policyStatements.map((stmt, i) => {
const effect = stmt.Effect;
const isAllow = effect === "Allow";
return (
<Box
className="policy-row"
key={`${i}`}
sx={{
display: "grid",
gridTemplateColumns: {
sm: "1fr 1fr",
xs: "1fr",
},
gridTemplateColumns: "1fr",
gap: "15px",
fontSize: "14px",
padding: "10px 0 10px 0",
"& .label": {
fontWeight: 600,
},
}}
>
<Box sx={rowGridStyle}>
<Box className="label">Actions:</Box>
<Box>
{stmt.Action &&
stmt.Action.map((act, actIndex) => (
<div key={`${i}-r-${actIndex}`}>
<Highlight search={filter}>{act}</Highlight>
</div>
))}
<Box className="label">Effect:</Box>
<Box
sx={{
display: "flex",
alignItems: "center",
"& .min-icon": {
marginRight: "5px",
fill: isAllow ? STATUS_COLORS.GREEN : STATUS_COLORS.RED,
height: "14px",
width: "14px",
},
}}
>
{isAllow ? <EnabledIcon /> : <DisabledIcon />}
{effect}
</Box>
</Box>
<Box sx={rowGridStyle}>
<Box className="label">Resources:</Box>
<Box>
{stmt.Resource &&
stmt.Resource.map((res, resIndex) => (
<div key={`${i}-r-${resIndex}`}>
{" "}
<Highlight search={filter}>{res}</Highlight>
</div>
))}
<Box
sx={{
display: "grid",
gridTemplateColumns: {
sm: "1fr 1fr",
xs: "1fr",
},
gap: "15px",
}}
>
<Box sx={rowGridStyle}>
<Box className="label">Actions:</Box>
<Box>
{stmt.Action &&
stmt.Action.map((act, actIndex) => (
<div key={`${i}-r-${actIndex}`}>
<Highlight search={filter}>{act}</Highlight>
</div>
))}
</Box>
</Box>
<Box sx={rowGridStyle}>
<Box className="label">Resources:</Box>
<Box>
{stmt.Resource &&
stmt.Resource.map((res, resIndex) => (
<div key={`${i}-r-${resIndex}`}>
{" "}
<Highlight search={filter}>{res}</Highlight>
</div>
))}
</Box>
</Box>
</Box>
</Box>
</Box>
);
})}
</Grid>
);
})}
</Grid>
)}
</Grid>
);
};
export default withStyles(styles)(PolicyView);
export default PolicyView;

View File

@@ -14,30 +14,12 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
export interface Policy {
name: string;
policy: string;
}
export interface PolicyList {
policies: Policy[];
total: number;
}
export interface IAMStatement {
Effect: string;
Action: string[];
Resource: string[];
}
export const newStatement = (): IAMStatement => {
return {
Effect: "Deny",
Action: [],
Resource: [],
};
};
export interface IAMPolicy {
Version: string;
Statement: IAMStatement[];

View File

@@ -18,7 +18,7 @@ import { Action } from "kbar/lib/types";
import { BucketsIcon } from "mds";
import { validRoutes } from "./valid-routes";
import { IAM_PAGES } from "../../common/SecureComponent/permissions";
import { Bucket } from "./Buckets/types";
import { Bucket } from "../../api/consoleApi";
export const routesAsKbarActions = (
features: string[] | null,

View File

@@ -14,16 +14,13 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { Policy } from "../api/consoleApi";
import { redirectRule } from "../screens/LoginPage/types";
interface userInterface {
accessKey: string;
}
interface policyInterface {
name: string;
}
interface policyDetailsInterface {
policy: string;
}
@@ -39,11 +36,11 @@ export const usersSort = (a: userInterface, b: userInterface) => {
return 0;
};
export const policySort = (a: policyInterface, b: policyInterface) => {
if (a.name > b.name) {
export const policySort = (a: Policy, b: Policy) => {
if (a.name! > b.name!) {
return 1;
}
if (a.name < b.name) {
if (a.name! < b.name!) {
return -1;
}
// a must be equal to b