diff --git a/public/assets/account-seeds.json b/public/assets/account-seeds.json new file mode 100644 index 000000000..5abc6d5c5 --- /dev/null +++ b/public/assets/account-seeds.json @@ -0,0 +1,230 @@ +[ + { + "role": { + "roleName": "storage-manager-role", + "trustPolicy": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { "Federated": "keycloak.zenko.local" }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + "keycloak:roles": "StorageManager" + } + } + } + ] + } + }, + "permissionPolicy": { + "policyName": "storage-manager-policy", + "policyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "FullAccess", + "Effect": "Allow", + "Action": ["*"], + "Resource": "*" + } + ] + } + } + }, + { + "role": { + "roleName": "storage-account-owner-role", + "trustPolicy": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { "Federated": "keycloak.zenko.local" }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { "keycloak:groups": "StorageAccountOwner" } + } + } + ] + } + }, + "permissionPolicy": { + "policyName": "storage-account-owner-policy", + "policyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Sid": "FullAccess", + "Effect": "Allow", + "Action": ["s3:*", "iam:*"], + "Resource": ["*"] + } + ] + } + } + }, + { + "role": { + "roleName": "data-consumer-role", + "trustPolicy": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { "Federated": "keycloak.zenko.local" }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { "keycloak:groups": "DataConsumer" } + } + } + ] + } + }, + "permissionPolicy": { + "policyName": "data-consumer-policy", + "policyDocument": { + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": "s3:ListAllMyBuckets", + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": ["s3:ListBucket", "s3:GetBucketLocation"], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": [ + "s3:MetadataSearch", + "s3:PutObject", + "s3:PutObjectAcl", + "s3:GetObject", + "s3:GetObjectAcl", + "s3:DeleteObject", + "s3:RestoreObject", + "s3:GetBucketVersioning", + "s3:GetBucketCors", + "s3:GetBucketAcl", + "s3:GetBucketObjectLockConfiguration", + "s3:ListObjectsV2", + "s3:ListObjectVersions", + "s3:PutObjectLockConfiguration", + "s3:DeleteObjects", + "s3:GetObjectRetention", + "s3:GetObjectLegalHold", + "s3:PutObjectRetention", + "s3:PutObjectLegalHold", + "s3:HeadObject", + "s3:CopyObject", + "s3:GetObjectTagging", + "s3:PutObjectTagging", + "s3:GetReplicationConfiguration", + "s3:GetLifecycleConfiguration", + "s3:DeleteObjectVersion", + "s3:PutLifecycleConfiguration", + "s3:PutReplicationConfiguration", + "s3:ListObjectVersion", + "s3:GetObjectVersion", + "s3:GetObjectVersionRetention", + "s3:GetObjectVersionLegalHold", + "s3:PutObjectVersionRetention", + "s3:PutObjectVersionLegalHold", + "s3:GetObjectVersionTagging", + "s3:DeleteObjectVersionTagging", + "s3:PutObjectVersionTagging", + "s3:GetObjectVersionAcl", + "s3:PutObjectVersionAcl", + "s3:GetBucketTagging", + "s3:PutBucketTagging", + "s3:DeleteBucketTagging" + ], + "Resource": "*" + } + ] + } + } + }, + { + "permissionPolicy": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetLifecycleConfiguration", + "s3:GetBucketVersioning", + "s3:ListBucket", + "s3:ListBucketVersions", + "s3:ListBucketMultipartUploads", + "s3:GetObjectTagging", + "s3:GetObjectVersionTagging", + "s3:GetObject", + "s3:GetObjectVersion" + ], + "Effect": "Allow", + "Resource": ["*"], + "Sid": "LifecycleExpirationBucketProcessor" + } + ], + "Version": "2012-10-17" + }, + "policyName": "backbeat-lifecycle-bp-1" + }, + "role": { + "roleName": "backbeat-lifecycle-bp-1", + "trustPolicy": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::000000000000:user/scality-internal/backbeat-lifecycle-bp-1" + } + } + ], + "Version": "2012-10-17" + } + } + }, + { + "permissionPolicy": { + "policyDocument": { + "Statement": [ + { + "Action": [ + "s3:GetObject", + "s3:GetObjectVersion", + "s3:DeleteObject", + "s3:DeleteObjectVersion", + "s3:AbortMultipartUpload" + ], + "Effect": "Allow", + "Resource": ["*"], + "Sid": "LifecycleExpirationObjectProcessor" + } + ], + "Version": "2012-10-17" + }, + "policyName": "backbeat-lifecycle-op-1" + }, + "role": { + "roleName": "backbeat-lifecycle-op-1", + "trustPolicy": { + "Statement": [ + { + "Action": "sts:AssumeRole", + "Effect": "Allow", + "Principal": { + "AWS": "arn:aws:iam::000000000000:user/scality-internal/backbeat-lifecycle-op-1" + } + } + ], + "Version": "2012-10-17" + } + } + } +] diff --git a/src/js/vault.ts b/src/js/vault.ts new file mode 100644 index 000000000..399268061 --- /dev/null +++ b/src/js/vault.ts @@ -0,0 +1,11 @@ +// TODO: AccountSeeds should be returned by Vault API +export function getAccountSeeds() { + return fetch('/account-seeds.json', { + headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, + }).then((res) => { + if (!res.ok) { + throw Error('Can not fetch Account Seeds!!'); + } + return res.json(); + }); +} diff --git a/src/react/account/iamAttachment/AttachmentTable.tsx b/src/react/account/iamAttachment/AttachmentTable.tsx index b0fdace4e..79089a2c7 100644 --- a/src/react/account/iamAttachment/AttachmentTable.tsx +++ b/src/react/account/iamAttachment/AttachmentTable.tsx @@ -524,6 +524,7 @@ export const AttachmentTable = < icon={} label="Remove" variant="danger" + disabled={!!entity.disableDetach} /> ), }, diff --git a/src/react/account/iamAttachment/AttachmentTabs.tsx b/src/react/account/iamAttachment/AttachmentTabs.tsx index 236d7a734..108a7c352 100644 --- a/src/react/account/iamAttachment/AttachmentTabs.tsx +++ b/src/react/account/iamAttachment/AttachmentTabs.tsx @@ -2,8 +2,10 @@ import { Loader } from '@scality/core-ui'; import { useReducer } from 'react'; import { useLocation, useParams } from 'react-router'; import { useTheme } from 'styled-components'; +import { useQuery } from 'react-query'; import { useIAMClient } from '../../IAMProvider'; import { + getAccountSeedsQuery, getListAttachedUserPoliciesQuery, getListEntitiesForPolicyQuery, getListGroupsQuery, @@ -95,10 +97,12 @@ const AttachmentTableProxy = < const AttachmentTabs = ({ resourceType, resourceId, + resourceName, onAttachmentsOperationsChanged, }: { resourceType: ResourceType; resourceId: string; + resourceName: string; onAttachmentsOperationsChanged: ( attachmentOperations: AttachmentOperation[], ) => void; @@ -133,6 +137,12 @@ const AttachmentTabs = ({ tabLineColor: backgroundLevel4, }; + const { data: accountSeeds } = useQuery(getAccountSeedsQuery()); + const policyRolePair = + accountSeeds?.filter( + (seed) => seed.permissionPolicy.policyName === resourceName, + ) || []; + return ( {resourceType === 'policy' && ( @@ -289,10 +299,15 @@ const AttachmentTabs = ({ getAttachedEntitesFromResult={(response) => { return ( response.PolicyRoles?.map((role) => { + const disableDetach = + !!policyRolePair.find( + (pair) => pair.role.roleName === role.RoleName, + ) !== undefined; return { name: role.RoleName || '', id: role.RoleName || '', type: 'role', + disableDetach, }; }) || [] ); diff --git a/src/react/account/iamAttachment/AttachmentTypes.ts b/src/react/account/iamAttachment/AttachmentTypes.ts index 34a7dc693..153b82d81 100644 --- a/src/react/account/iamAttachment/AttachmentTypes.ts +++ b/src/react/account/iamAttachment/AttachmentTypes.ts @@ -5,6 +5,7 @@ export type AttachableEntity = { name: string; id: string; type: EntityType; + disableDetach?: boolean; }; export enum AttachmentAction { diff --git a/src/react/account/iamAttachment/Attachments.tsx b/src/react/account/iamAttachment/Attachments.tsx index 70d19f46b..2ad6ff0dd 100644 --- a/src/react/account/iamAttachment/Attachments.tsx +++ b/src/react/account/iamAttachment/Attachments.tsx @@ -1,5 +1,4 @@ import { BasicText, EmphaseText, LargerText } from '@scality/core-ui'; -import { Button } from '@scality/core-ui/dist/next'; import { useState } from 'react'; import { useParams, useRouteMatch } from 'react-router'; import styled from 'styled-components'; @@ -75,6 +74,7 @@ const Attachments = () => { @@ -84,7 +84,11 @@ const Attachments = () => { resourceId={resourceId} resourceName={resourceName} resourceType={resourceType} - redirectUrl={isAttachToPolicy ? `/accounts/${account?.Name}/policies` : `/accounts/${account?.Name}/users`} + redirectUrl={ + isAttachToPolicy + ? `/accounts/${account?.Name}/policies` + : `/accounts/${account?.Name}/users` + } /> diff --git a/src/react/queries.ts b/src/react/queries.ts index c4107bb85..8a16fab64 100644 --- a/src/react/queries.ts +++ b/src/react/queries.ts @@ -4,6 +4,7 @@ import { APIWorkflows, Workflows, Workflow } from '../types/workflow'; import { generateExpirationName, generateStreamName } from './workflow/utils'; import IAMClient from '../js/IAMClient'; import { QueryFunctionContext } from 'react-query'; +import { getAccountSeeds } from '../js/vault'; // Copy paste form legacy redux workflow export const makeWorkflows = (apiWorkflows: APIWorkflows): Workflows => { @@ -161,3 +162,8 @@ export const getListAttachedUserPoliciesQuery = ( refetchOnMount: false, refetchOnWindowFocus: false, }); + +export const getAccountSeedsQuery = () => ({ + queryKey: ['AccountSeeds'], + queryFn: getAccountSeeds, +});