Adding SecureComponent to policy details page (#1251)
- Make Users & Policies clickeable only if user has permissions to view in bucket page - Add SecureComponent to policy detail page: api calls, menu options, raw policy editor, etc. - Add missing click action to groups in policy detail page - Fix NPE in list groups for policy endpoint - Added SecureComponent to ListPolicies page Signed-off-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
@@ -117,6 +117,10 @@ export const IAM_SCOPES = {
|
||||
ADMIN_GET_GROUP: "admin:GetGroup",
|
||||
ADMIN_ENABLE_GROUP: "admin:EnableGroup",
|
||||
ADMIN_DISABLE_GROUP: "admin:DisableGroup",
|
||||
ADMIN_GET_USER: "admin:GetUser",
|
||||
ADMIN_CREATE_POLICY: "admin:CreatePolicy",
|
||||
ADMIN_DELETE_POLICY: "admin:DeletePolicy",
|
||||
ADMIN_ATTACH_USER_OR_GROUP_POLICY: "admin:AttachUserOrGroupPolicy",
|
||||
S3_ALL_ACTIONS: "s3:*",
|
||||
ADMIN_ALL_ACTIONS: "admin:*",
|
||||
};
|
||||
|
||||
@@ -30,7 +30,10 @@ import TableWrapper from "../../Common/TableWrapper/TableWrapper";
|
||||
import api from "../../../../common/api";
|
||||
import history from "../../../../history";
|
||||
import { BucketInfo } from "../types";
|
||||
import { IAM_SCOPES } from "../../../../common/SecureComponent/permissions";
|
||||
import {
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_SCOPES,
|
||||
} from "../../../../common/SecureComponent/permissions";
|
||||
import PanelTitle from "../../Common/PanelTitle/PanelTitle";
|
||||
import SecureComponent, {
|
||||
hasPermission,
|
||||
@@ -90,6 +93,15 @@ const AccessDetails = ({
|
||||
true
|
||||
);
|
||||
|
||||
const viewUser = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_GET_USER,
|
||||
]);
|
||||
const viewPolicy = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_GET_POLICY,
|
||||
IAM_SCOPES.ADMIN_LIST_USERS,
|
||||
IAM_SCOPES.ADMIN_LIST_GROUPS,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadingBucket) {
|
||||
setLoadingUsers(true);
|
||||
@@ -100,6 +112,7 @@ const AccessDetails = ({
|
||||
const PolicyActions = [
|
||||
{
|
||||
type: "view",
|
||||
disableButtonFunction: () => !viewPolicy,
|
||||
onClick: (policy: any) => {
|
||||
history.push(`/policies/${policy.name}`);
|
||||
},
|
||||
@@ -109,6 +122,7 @@ const AccessDetails = ({
|
||||
const userTableActions = [
|
||||
{
|
||||
type: "view",
|
||||
disableButtonFunction: () => !viewUser,
|
||||
onClick: (user: any) => {
|
||||
history.push(`/users/${user}`);
|
||||
},
|
||||
|
||||
@@ -42,6 +42,13 @@ import history from "../../../history";
|
||||
import SearchIcon from "../../../icons/SearchIcon";
|
||||
import HelpBox from "../../../common/HelpBox";
|
||||
import PageLayout from "../Common/Layout/PageLayout";
|
||||
import {
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_SCOPES,
|
||||
} from "../../../common/SecureComponent/permissions";
|
||||
import SecureComponent, {
|
||||
hasPermission,
|
||||
} from "../../../common/SecureComponent/SecureComponent";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -91,38 +98,54 @@ const ListPolicies = ({ classes, setErrorSnackMessage }: IPoliciesProps) => {
|
||||
const [filterPolicies, setFilterPolicies] = useState<string>("");
|
||||
const [policyEdit, setPolicyEdit] = useState<any>(null);
|
||||
|
||||
const viewPolicy = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_GET_POLICY,
|
||||
]);
|
||||
|
||||
const deletePolicy = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_DELETE_POLICY,
|
||||
]);
|
||||
|
||||
const displayPolicies = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_LIST_USER_POLICIES,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchRecords();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/policies`)
|
||||
.then((res: PolicyList) => {
|
||||
const policies = get(res, "policies", []);
|
||||
if (displayPolicies) {
|
||||
api
|
||||
.invoke("GET", `/api/v1/policies`)
|
||||
.then((res: PolicyList) => {
|
||||
const policies = get(res, "policies", []);
|
||||
|
||||
policies.sort((pa, pb) => {
|
||||
if (pa.name > pb.name) {
|
||||
return 1;
|
||||
}
|
||||
policies.sort((pa, pb) => {
|
||||
if (pa.name > pb.name) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pa.name < pb.name) {
|
||||
return -1;
|
||||
}
|
||||
if (pa.name < pb.name) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return 0;
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
setRecords(policies);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoading(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
|
||||
setLoading(false);
|
||||
setRecords(policies);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setLoading(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
} else {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}, [loading, setLoading, setRecords, setErrorSnackMessage]);
|
||||
}, [loading, setLoading, setRecords, setErrorSnackMessage, displayPolicies]);
|
||||
|
||||
const fetchRecords = () => {
|
||||
setLoading(true);
|
||||
@@ -154,8 +177,17 @@ const ListPolicies = ({ classes, setErrorSnackMessage }: IPoliciesProps) => {
|
||||
};
|
||||
|
||||
const tableActions = [
|
||||
{ type: "view", onClick: viewAction },
|
||||
{ type: "delete", onClick: confirmDeletePolicy, sendOnlyId: true },
|
||||
{
|
||||
type: "view",
|
||||
onClick: viewAction,
|
||||
disableButtonFunction: () => !viewPolicy,
|
||||
},
|
||||
{
|
||||
type: "delete",
|
||||
onClick: confirmDeletePolicy,
|
||||
sendOnlyId: true,
|
||||
disableButtonFunction: () => !deletePolicy,
|
||||
},
|
||||
];
|
||||
|
||||
const filteredRecords = records.filter((elementItem) =>
|
||||
@@ -199,30 +231,41 @@ const ListPolicies = ({ classes, setErrorSnackMessage }: IPoliciesProps) => {
|
||||
}}
|
||||
variant="standard"
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<AddIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
setPolicyEdit(null);
|
||||
}}
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.ADMIN_CREATE_POLICY]}
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
>
|
||||
Create Policy
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
endIcon={<AddIcon />}
|
||||
onClick={() => {
|
||||
setAddScreenOpen(true);
|
||||
setPolicyEdit(null);
|
||||
}}
|
||||
>
|
||||
Create Policy
|
||||
</Button>
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Policies"
|
||||
idField="name"
|
||||
/>
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.ADMIN_LIST_USER_POLICIES]}
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
errorProps={{ disabled: true }}
|
||||
>
|
||||
<TableWrapper
|
||||
itemActions={tableActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loading}
|
||||
records={filteredRecords}
|
||||
entityName="Policies"
|
||||
idField="name"
|
||||
/>
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<HelpBox
|
||||
|
||||
@@ -48,6 +48,13 @@ import PageLayout from "../Common/Layout/PageLayout";
|
||||
import VerticalTabs from "../Common/VerticalTabs/VerticalTabs";
|
||||
import BackLink from "../../../common/BackLink";
|
||||
import BoxIconButton from "../Common/BoxIconButton/BoxIconButton";
|
||||
import {
|
||||
CONSOLE_UI_RESOURCE,
|
||||
IAM_SCOPES,
|
||||
} from "../../../common/SecureComponent/permissions";
|
||||
import SecureComponent, {
|
||||
hasPermission,
|
||||
} from "../../../common/SecureComponent/SecureComponent";
|
||||
|
||||
interface IPolicyDetailsProps {
|
||||
classes: any;
|
||||
@@ -209,84 +216,129 @@ const PolicyDetails = ({
|
||||
const [loadingGroups, setLoadingGroups] = useState<boolean>(true);
|
||||
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
|
||||
|
||||
const displayGroups = hasPermission(
|
||||
CONSOLE_UI_RESOURCE,
|
||||
[IAM_SCOPES.ADMIN_LIST_GROUPS, IAM_SCOPES.ADMIN_GET_GROUP],
|
||||
true
|
||||
);
|
||||
|
||||
const viewGroup = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_GET_GROUP,
|
||||
]);
|
||||
|
||||
const displayUsers = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_LIST_GROUPS,
|
||||
]);
|
||||
|
||||
const viewUser = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_GET_USER,
|
||||
]);
|
||||
|
||||
const displayPolicy = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_GET_POLICY,
|
||||
]);
|
||||
|
||||
const editPolicy = hasPermission(CONSOLE_UI_RESOURCE, [
|
||||
IAM_SCOPES.ADMIN_CREATE_POLICY,
|
||||
]);
|
||||
|
||||
const saveRecord = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
if (addLoading) {
|
||||
return;
|
||||
}
|
||||
setAddLoading(true);
|
||||
api
|
||||
.invoke("POST", "/api/v1/policies", {
|
||||
name: policyName,
|
||||
policy: policyDefinition,
|
||||
})
|
||||
.then((_) => {
|
||||
setAddLoading(false);
|
||||
setSnackBarMessage("Policy successfully updated");
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setAddLoading(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
if (editPolicy) {
|
||||
api
|
||||
.invoke("POST", "/api/v1/policies", {
|
||||
name: policyName,
|
||||
policy: policyDefinition,
|
||||
})
|
||||
.then((_) => {
|
||||
setAddLoading(false);
|
||||
setSnackBarMessage("Policy successfully updated");
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setAddLoading(false);
|
||||
setErrorSnackMessage(err);
|
||||
});
|
||||
} else {
|
||||
setAddLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const loadUsersForPolicy = () => {
|
||||
if (loadingUsers) {
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/policies/${encodeURIComponent(policyName)}/users`
|
||||
)
|
||||
.then((result: any) => {
|
||||
setUserList(result);
|
||||
setLoadingUsers(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setErrorSnackMessage(err);
|
||||
setLoadingUsers(false);
|
||||
});
|
||||
if (displayUsers) {
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/policies/${encodeURIComponent(policyName)}/users`
|
||||
)
|
||||
.then((result: any) => {
|
||||
setUserList(result);
|
||||
setLoadingUsers(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setErrorSnackMessage(err);
|
||||
setLoadingUsers(false);
|
||||
});
|
||||
} else {
|
||||
setLoadingUsers(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const loadGroupsForPolicy = () => {
|
||||
if (loadingGroups) {
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/policies/${encodeURIComponent(policyName)}/groups`
|
||||
)
|
||||
.then((result: any) => {
|
||||
setGroupList(result);
|
||||
setLoadingGroups(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setErrorSnackMessage(err);
|
||||
setLoadingGroups(false);
|
||||
});
|
||||
if (displayGroups) {
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/policies/${encodeURIComponent(policyName)}/groups`
|
||||
)
|
||||
.then((result: any) => {
|
||||
setGroupList(result);
|
||||
setLoadingGroups(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setErrorSnackMessage(err);
|
||||
setLoadingGroups(false);
|
||||
});
|
||||
} else {
|
||||
setLoadingGroups(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
const loadPolicyDetails = () => {
|
||||
if (loadingPolicy) {
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/policy?name=${encodeURIComponent(policyName)}`
|
||||
)
|
||||
.then((result: any) => {
|
||||
if (result) {
|
||||
setPolicy(result);
|
||||
setPolicyDefinition(
|
||||
result ? JSON.stringify(JSON.parse(result.policy), null, 4) : ""
|
||||
);
|
||||
const pol: IAMPolicy = JSON.parse(result.policy);
|
||||
setPolicyStatements(pol.Statement);
|
||||
}
|
||||
setLoadingPolicy(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setErrorSnackMessage(err);
|
||||
setLoadingPolicy(false);
|
||||
});
|
||||
if (displayPolicy) {
|
||||
api
|
||||
.invoke(
|
||||
"GET",
|
||||
`/api/v1/policy?name=${encodeURIComponent(policyName)}`
|
||||
)
|
||||
.then((result: any) => {
|
||||
if (result) {
|
||||
setPolicy(result);
|
||||
setPolicyDefinition(
|
||||
result
|
||||
? JSON.stringify(JSON.parse(result.policy), null, 4)
|
||||
: ""
|
||||
);
|
||||
const pol: IAMPolicy = JSON.parse(result.policy);
|
||||
setPolicyStatements(pol.Statement);
|
||||
}
|
||||
setLoadingPolicy(false);
|
||||
})
|
||||
.catch((err: ErrorResponseHandler) => {
|
||||
setErrorSnackMessage(err);
|
||||
setLoadingPolicy(false);
|
||||
});
|
||||
} else {
|
||||
setLoadingPolicy(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -307,6 +359,9 @@ const PolicyDetails = ({
|
||||
setPolicy,
|
||||
setLoadingUsers,
|
||||
setLoadingGroups,
|
||||
displayUsers,
|
||||
displayGroups,
|
||||
displayPolicy,
|
||||
]);
|
||||
|
||||
const resetForm = () => {
|
||||
@@ -328,12 +383,30 @@ const PolicyDetails = ({
|
||||
const userViewAction = (user: any) => {
|
||||
history.push(`/users/${user}`);
|
||||
};
|
||||
const userTableActions = [{ type: "view", onClick: userViewAction }];
|
||||
const userTableActions = [
|
||||
{
|
||||
type: "view",
|
||||
onClick: userViewAction,
|
||||
disableButtonFunction: () => !viewUser,
|
||||
},
|
||||
];
|
||||
|
||||
const filteredUsers = userList.filter((elementItem) =>
|
||||
elementItem.includes(filterUsers)
|
||||
);
|
||||
|
||||
const groupViewAction = (group: any) => {
|
||||
history.push(`/groups/${group}`);
|
||||
};
|
||||
|
||||
const groupTableActions = [
|
||||
{
|
||||
type: "view",
|
||||
onClick: groupViewAction,
|
||||
disableButtonFunction: () => !viewGroup,
|
||||
},
|
||||
];
|
||||
|
||||
const filteredGroups = groupList.filter((elementItem) =>
|
||||
elementItem.includes(filterGroups)
|
||||
);
|
||||
@@ -373,14 +446,20 @@ const PolicyDetails = ({
|
||||
subTitle={<Fragment>IAM Policy</Fragment>}
|
||||
actions={
|
||||
<Fragment>
|
||||
<BoxIconButton
|
||||
tooltip={"Delete Policy"}
|
||||
color="primary"
|
||||
aria-label="Delete Policy"
|
||||
onClick={deletePolicy}
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.ADMIN_DELETE_POLICY]}
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
>
|
||||
<TrashIcon />
|
||||
</BoxIconButton>
|
||||
<BoxIconButton
|
||||
tooltip={"Delete Policy"}
|
||||
color="primary"
|
||||
aria-label="Delete Policy"
|
||||
onClick={deletePolicy}
|
||||
>
|
||||
<TrashIcon />
|
||||
</BoxIconButton>
|
||||
</SecureComponent>
|
||||
|
||||
<BoxIconButton
|
||||
tooltip={"Refresh"}
|
||||
color="primary"
|
||||
@@ -401,7 +480,7 @@ const PolicyDetails = ({
|
||||
|
||||
<VerticalTabs>
|
||||
{{
|
||||
tabConfig: { label: "Summary" },
|
||||
tabConfig: { label: "Summary", disabled: !displayPolicy },
|
||||
content: (
|
||||
<Fragment>
|
||||
<div className={classes.sectionTitle}>Policy Summary</div>
|
||||
@@ -479,7 +558,7 @@ const PolicyDetails = ({
|
||||
),
|
||||
}}
|
||||
{{
|
||||
tabConfig: { label: "Users" },
|
||||
tabConfig: { label: "Users", disabled: !displayUsers },
|
||||
content: (
|
||||
<Fragment>
|
||||
<div className={classes.sectionTitle}>Users</div>
|
||||
@@ -520,7 +599,7 @@ const PolicyDetails = ({
|
||||
),
|
||||
}}
|
||||
{{
|
||||
tabConfig: { label: "Groups" },
|
||||
tabConfig: { label: "Groups", disabled: !displayGroups },
|
||||
content: (
|
||||
<Fragment>
|
||||
<div className={classes.sectionTitle}>Groups</div>
|
||||
@@ -549,20 +628,19 @@ const PolicyDetails = ({
|
||||
<br />
|
||||
</Grid>
|
||||
<TableWrapper
|
||||
itemActions={[]}
|
||||
itemActions={groupTableActions}
|
||||
columns={[{ label: "Name", elementKey: "name" }]}
|
||||
isLoading={loadingGroups}
|
||||
records={filteredGroups}
|
||||
entityName="Groups"
|
||||
idField="name"
|
||||
/* customPaperHeight={classes.tableHeight}*/
|
||||
/>
|
||||
</Grid>
|
||||
</Fragment>
|
||||
),
|
||||
}}
|
||||
{{
|
||||
tabConfig: { label: "Raw Policy" },
|
||||
tabConfig: { label: "Raw Policy", disabled: !displayPolicy },
|
||||
content: (
|
||||
<Fragment>
|
||||
<div className={classes.sectionTitle}>Raw Policy</div>
|
||||
@@ -577,6 +655,7 @@ const PolicyDetails = ({
|
||||
<Grid container>
|
||||
<Grid item xs={12} className={classes.formScrollable}>
|
||||
<CodeMirrorWrapper
|
||||
readOnly={!editPolicy}
|
||||
value={policyDefinition}
|
||||
onBeforeChange={(editor, data, value) => {
|
||||
setPolicyDefinition(value);
|
||||
@@ -596,15 +675,19 @@ const PolicyDetails = ({
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading || !validSave}
|
||||
<SecureComponent
|
||||
scopes={[IAM_SCOPES.ADMIN_CREATE_POLICY]}
|
||||
resource={CONSOLE_UI_RESOURCE}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading || !validSave}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</SecureComponent>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
|
||||
@@ -308,7 +308,7 @@ func getListGroupsForPolicyResponse(session *models.Principal, policy string) ([
|
||||
for _, group := range groups {
|
||||
info, err := groupInfo(ctx, adminClient, group)
|
||||
if err != nil {
|
||||
LogError("unable to fetch group info %s: %v", group, err)
|
||||
return nil, prepareError(err)
|
||||
}
|
||||
if info.Policy == policy {
|
||||
filteredGroups = append(filteredGroups, group)
|
||||
|
||||
Reference in New Issue
Block a user