diff --git a/.circleci/test-console.sh b/.circleci/test-console.sh index 67fd0bd2754..486de45b70f 100755 --- a/.circleci/test-console.sh +++ b/.circleci/test-console.sh @@ -24,7 +24,7 @@ touch /build/_console_output/cli.log # start graphql-engine /build/_server_output/graphql-engine \ - --database-url postgres://gql_test@localhost:5432/gql_test serve > /build/_console_output/server.log 2>&1 & + --database-url postgres://gql_test@localhost:5432/gql_test serve --enable-remote-schema-permissions > /build/_console_output/server.log 2>&1 & wait_for_port 8080 diff --git a/CHANGELOG.md b/CHANGELOG.md index b55bd83bdeb..4f0adbff2a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ - server: fix issue with parsing of remote schema list of input objects (fix #6584) - cli: cli-ext is now a native part of cli binary (no longer needed as a plugin) - console: add browse rows for mssql tables (#805) - +- console: remote schema permissions bug fixes (#439) ## v2.0.0-alpha.4 diff --git a/console/cypress/integration/remote-schemas/create-remote-schema/spec.ts b/console/cypress/integration/remote-schemas/create-remote-schema/spec.ts index c9d97c73efb..aabf5864144 100644 --- a/console/cypress/integration/remote-schemas/create-remote-schema/spec.ts +++ b/console/cypress/integration/remote-schemas/create-remote-schema/spec.ts @@ -299,6 +299,7 @@ export const createSimpleRemoteSchemaPermission = () => { testName )}/permissions` ); + cy.get(getElementFromAlias('role-test-role-rs-1')).should('be.visible'); cy.wait(5000); }; diff --git a/console/src/components/Common/Permissions/TableRow.js b/console/src/components/Common/Permissions/TableRow.js index 5485ae08789..dcfa33b22ef 100644 --- a/console/src/components/Common/Permissions/TableRow.js +++ b/console/src/components/Common/Permissions/TableRow.js @@ -25,7 +25,11 @@ const TableRow = ({ ); } else { - rowCells.push({roleName}); + rowCells.push( + + {roleName} + + ); } permTypes.forEach(p => { diff --git a/console/src/components/Main/State.ts b/console/src/components/Main/State.ts index ca321468fa5..d49027b2fe6 100644 --- a/console/src/components/Main/State.ts +++ b/console/src/components/Main/State.ts @@ -21,6 +21,7 @@ export interface MainState { is_function_permissions_inferred: boolean; is_admin_secret_set: boolean; is_auth_hook_set: boolean; + is_remote_schema_permissions_enabled: boolean; is_jwt_set: boolean; experimental_features: string[]; jwt: { @@ -59,6 +60,7 @@ const defaultState: MainState = { is_function_permissions_inferred: true, is_admin_secret_set: false, is_auth_hook_set: false, + is_remote_schema_permissions_enabled: false, experimental_features: [], is_jwt_set: false, jwt: { diff --git a/console/src/components/Services/RemoteSchema/Permissions/Field.tsx b/console/src/components/Services/RemoteSchema/Permissions/Field.tsx index da68c90d520..f0d5df297f8 100644 --- a/console/src/components/Services/RemoteSchema/Permissions/Field.tsx +++ b/console/src/components/Services/RemoteSchema/Permissions/Field.tsx @@ -83,7 +83,7 @@ export const Field: React.FC = ({ > {i.name} - {i.args && ' ('} + {i.args && Object.keys(i.args).length !== 0 && ' ('} {i.args && ( )} - {i.args && ' )'} + {i.args && Object.keys(i.args).length !== 0 && ' )'} {i.return && ( : diff --git a/console/src/components/Services/RemoteSchema/Permissions/Permissions.tsx b/console/src/components/Services/RemoteSchema/Permissions/Permissions.tsx index a871d2566ec..5e7e1a7098f 100644 --- a/console/src/components/Services/RemoteSchema/Permissions/Permissions.tsx +++ b/console/src/components/Services/RemoteSchema/Permissions/Permissions.tsx @@ -128,6 +128,7 @@ const Permissions: React.FC = props => { currentRemoteSchema={currentRemoteSchema} permissionEdit={permissionEdit} isEditing={isEditing} + schema={schema} bulkSelect={bulkSelect} readOnlyMode={readOnlyMode} permSetRoleName={permSetRoleName} diff --git a/console/src/components/Services/RemoteSchema/Permissions/PermissionsTable.tsx b/console/src/components/Services/RemoteSchema/Permissions/PermissionsTable.tsx index c6b30365035..ad9d373c2d7 100644 --- a/console/src/components/Services/RemoteSchema/Permissions/PermissionsTable.tsx +++ b/console/src/components/Services/RemoteSchema/Permissions/PermissionsTable.tsx @@ -1,9 +1,14 @@ import React, { ChangeEvent } from 'react'; +import { GraphQLSchema } from 'graphql'; import styles from '../../../Common/Permissions/PermissionStyles.scss'; import PermTableHeader from '../../../Common/Permissions/TableHeader'; import PermTableBody from '../../../Common/Permissions/TableBody'; import { permissionsSymbols } from '../../../Common/Permissions/PermissionSymbols'; -import { findRemoteSchemaPermission } from './utils'; +import { + buildSchemaFromRoleDefn, + findRemoteSchemaPermission, + getRemoteSchemaFields, +} from './utils'; import { RolePermissions, PermOpenEditType, @@ -22,6 +27,7 @@ export type PermissionsTableProps = { name: string; permissions?: PermissionsType[]; }; + schema: GraphQLSchema; bulkSelect: string[]; readOnlyMode: boolean; permissionEdit: PermissionEdit; @@ -37,6 +43,7 @@ const PermissionsTable: React.FC = ({ isEditing, bulkSelect, readOnlyMode, + schema, permSetRoleName, permSetBulkSelect, setSchemaDefinition, @@ -139,10 +146,28 @@ const PermissionsTable: React.FC = ({ permissionAccess = permissionsSymbols.noAccess; } else { const existingPerm = findRemoteSchemaPermission(allPermissions, role); - if (!existingPerm) { - permissionAccess = permissionsSymbols.noAccess; - } else { + if (existingPerm) { + const remoteFields = getRemoteSchemaFields( + schema, + buildSchemaFromRoleDefn(existingPerm?.definition.schema) + ); permissionAccess = permissionsSymbols.fullAccess; + + if ( + remoteFields + .filter( + field => + !field.name.startsWith('enum') && + !field.name.startsWith('scalar') + ) + .some(field => + field.children?.some(element => element.checked === false) + ) + ) { + permissionAccess = permissionsSymbols.partialAccess; + } + } else { + permissionAccess = permissionsSymbols.noAccess; } } return permissionAccess; @@ -182,10 +207,13 @@ const PermissionsTable: React.FC = ({
- {permissionsSymbols.fullAccess} : allowed + {permissionsSymbols.fullAccess} : full access - {permissionsSymbols.noAccess} : not allowed + {permissionsSymbols.noAccess} : no access + + + {permissionsSymbols.partialAccess} : partial access
diff --git a/console/src/components/Services/RemoteSchema/Permissions/Tree.tsx b/console/src/components/Services/RemoteSchema/Permissions/Tree.tsx index bb974deba09..f19e32ee429 100644 --- a/console/src/components/Services/RemoteSchema/Permissions/Tree.tsx +++ b/console/src/components/Services/RemoteSchema/Permissions/Tree.tsx @@ -2,7 +2,7 @@ import React, { useCallback, useEffect, useRef, useState } from 'react'; import { FieldType, ExpandedItems, PermissionEdit } from './types'; import { Field } from './Field'; import styles from '../../../Common/Permissions/PermissionStyles.scss'; -import { addDepFields, getExpandeItems } from './utils'; +import { addDepFields, getExpandedItems } from './utils'; type RSPTreeComponentProps = { list: FieldType[]; @@ -51,14 +51,16 @@ const Tree: React.FC = ({ ); useEffect(() => { - const expandedItemsFromList = getExpandeItems(list); + const expandedItemsFromList = getExpandedItems(list); setExpandedItems({ ...expandedItems, ...expandedItemsFromList }); // this will only handle expand, it wont collapse anything which are already expanded. }, [list]); useEffect(() => { if ( - permissionEdit?.isNewRole && - permissionEdit?.isNewRole !== prevIsNewRole.current + permissionEdit && + (!permissionEdit?.isNewRole || + (permissionEdit?.isNewRole && + permissionEdit?.isNewRole !== prevIsNewRole.current)) ) { // ignore the new role name change event setExpandedItems({}); diff --git a/console/src/components/Services/RemoteSchema/Permissions/index.tsx b/console/src/components/Services/RemoteSchema/Permissions/index.tsx index 32778599571..6baaa43d205 100644 --- a/console/src/components/Services/RemoteSchema/Permissions/index.tsx +++ b/console/src/components/Services/RemoteSchema/Permissions/index.tsx @@ -22,30 +22,42 @@ import { rolesSelector, getRemoteSchemaPermissions, } from '../../../../metadata/selector'; -import { RemoteSchema } from '../../../../metadata/types'; export type RSPContainerProps = { - allRoles: string[]; - allRemoteSchemas: RemoteSchema[]; - params: { remoteSchemaName: string }; - viewRemoteSchema: (data: string) => void; + rspEnabled: boolean; }; const RSP: React.FC = props => { - const { allRoles, allRemoteSchemas, params, viewRemoteSchema } = props; + const { + allRoles, + allRemoteSchemas, + params, + viewRemoteSchema, + rspEnabled, + } = props; return ( ( - - )} + permissionRenderer={currentRemoteSchema => + rspEnabled ? ( + + ) : ( +
+ Remote schema permissions are not enabled. To enable remote schema + permissions, start the Hasura server with environment variable + + HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS: "true" + +
+ ) + } /> ); }; @@ -56,6 +68,8 @@ const mapStateToProps = (state: ReduxState) => { ...state.remoteSchemas, allRoles: rolesSelector(state), allRemoteSchemas: getRemoteSchemas(state), + rspEnabled: + state.main.serverConfig?.data?.is_remote_schema_permissions_enabled, readOnlyMode: state.main.readOnlyMode, }; }; @@ -89,7 +103,8 @@ const connector = connect(mapStateToProps, mapDispatchToProps); type InjectedProps = ConnectedProps; -type ComponentProps = RSPWrapperProps & PermissionsProps; +type ComponentProps = RSPWrapperProps & PermissionsProps & RSPContainerProps; + type Props = ComponentProps & InjectedProps; const RSPContainer = connector(RSP); diff --git a/console/src/components/Services/RemoteSchema/Permissions/utils.ts b/console/src/components/Services/RemoteSchema/Permissions/utils.ts index 652e118cdf7..30754956e87 100644 --- a/console/src/components/Services/RemoteSchema/Permissions/utils.ts +++ b/console/src/components/Services/RemoteSchema/Permissions/utils.ts @@ -767,7 +767,7 @@ export const addDepFields = (list: FieldType[], field: FieldType) => { return newList; }; -export const getExpandeItems = (list: FieldType[]) => { +export const getExpandedItems = (list: FieldType[]) => { const res: ExpandedItems = {}; list.forEach((item: FieldType, ix) => { const hasValidChildren = item?.children?.find(i => i.checked === true);