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:
Lenin Alevski
2021-11-23 20:09:36 -08:00
committed by GitHub
parent 06b08593b7
commit 0ea2193878
5 changed files with 268 additions and 124 deletions

View File

@@ -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:*",
};

View File

@@ -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}`);
},

View File

@@ -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

View File

@@ -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}>

View File

@@ -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)