mirror of
https://github.com/hasura/graphql-engine.git
synced 2024-12-14 08:02:15 +03:00
Dsf 248 permissions add view checks to permissions
PR-URL: https://github.com/hasura/graphql-engine-mono/pull/8600 GitOrigin-RevId: f877085e96cc75d1ba784095ef5a6701ddad9fcc
This commit is contained in:
parent
f93eed713f
commit
56beb705b2
@ -0,0 +1,26 @@
|
||||
import { DataSource } from '../../DataSource';
|
||||
import { useHttpClient } from '../../Network';
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
export const useIsTableView = ({
|
||||
dataSourceName,
|
||||
table,
|
||||
}: {
|
||||
dataSourceName: string;
|
||||
table: unknown;
|
||||
}) => {
|
||||
const httpClient = useHttpClient();
|
||||
return useQuery({
|
||||
queryKey: [dataSourceName, table, 'isView'],
|
||||
queryFn: async () => {
|
||||
const isView = await DataSource(httpClient).getIsTableView({
|
||||
dataSourceName,
|
||||
table,
|
||||
httpClient,
|
||||
});
|
||||
|
||||
return isView;
|
||||
},
|
||||
refetchOnWindowFocus: false,
|
||||
});
|
||||
};
|
@ -13,6 +13,7 @@ import {
|
||||
import { getTableRows } from '../postgres/query';
|
||||
import { runSQL } from '../api';
|
||||
import { postgresCapabilities } from '../common/capabilities';
|
||||
import { getIsTableView } from './introspection/getIsTableView';
|
||||
|
||||
export type AlloyDbTable = { name: string; schema: string };
|
||||
|
||||
@ -47,6 +48,7 @@ export const alloy: Database = {
|
||||
getTablesListAsTree,
|
||||
getSupportedOperators,
|
||||
getDatabaseSchemas,
|
||||
getIsTableView,
|
||||
},
|
||||
query: {
|
||||
getTableRows,
|
||||
|
@ -0,0 +1,30 @@
|
||||
import { runSQL } from '../../api';
|
||||
import { GetIsTableViewProps } from '../../types';
|
||||
import { AlloyDbTable } from '../index';
|
||||
|
||||
export const getIsTableView = async ({
|
||||
dataSourceName,
|
||||
table,
|
||||
httpClient,
|
||||
}: GetIsTableViewProps) => {
|
||||
const { schema, name } = table as AlloyDbTable;
|
||||
|
||||
const sql = `
|
||||
SELECT TABLE_NAME
|
||||
FROM information_schema.views
|
||||
WHERE TABLE_SCHEMA = '${schema}'
|
||||
AND TABLE_NAME = '${name}';`;
|
||||
|
||||
const views = await runSQL({
|
||||
source: {
|
||||
name: dataSourceName,
|
||||
kind: 'postgres',
|
||||
},
|
||||
sql: sql,
|
||||
readOnly: true,
|
||||
httpClient,
|
||||
});
|
||||
|
||||
if (Array.isArray(views?.result)) return views?.result?.length > 1;
|
||||
return false;
|
||||
};
|
@ -38,6 +38,7 @@ export const bigquery: Database = {
|
||||
getTablesListAsTree,
|
||||
getSupportedOperators,
|
||||
getDatabaseSchemas: async () => Feature.NotImplemented,
|
||||
getIsTableView: async () => Feature.NotImplemented,
|
||||
},
|
||||
query: {
|
||||
getTableRows,
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
getFKRelationships,
|
||||
getTablesListAsTree,
|
||||
getSupportedOperators,
|
||||
getIsTableView,
|
||||
} from './introspection';
|
||||
import { getTableRows } from './query';
|
||||
import { postgresCapabilities } from '../common/capabilities';
|
||||
@ -80,6 +81,7 @@ export const citus: Database = {
|
||||
getTablesListAsTree,
|
||||
getSupportedOperators,
|
||||
getDatabaseSchemas: async () => Feature.NotImplemented,
|
||||
getIsTableView,
|
||||
},
|
||||
query: {
|
||||
getTableRows,
|
||||
|
@ -0,0 +1,30 @@
|
||||
import { runSQL } from '../../api';
|
||||
import { GetIsTableViewProps } from '../../types';
|
||||
import { CitusTable } from '../index';
|
||||
|
||||
export const getIsTableView = async ({
|
||||
dataSourceName,
|
||||
table,
|
||||
httpClient,
|
||||
}: GetIsTableViewProps) => {
|
||||
const { schema, name } = table as CitusTable;
|
||||
|
||||
const sql = `
|
||||
SELECT TABLE_NAME
|
||||
FROM information_schema.views
|
||||
WHERE TABLE_SCHEMA = '${schema}'
|
||||
AND TABLE_NAME = '${name}';`;
|
||||
|
||||
const views = await runSQL({
|
||||
source: {
|
||||
name: dataSourceName,
|
||||
kind: 'postgres',
|
||||
},
|
||||
sql: sql,
|
||||
readOnly: true,
|
||||
httpClient,
|
||||
});
|
||||
|
||||
if (Array.isArray(views?.result)) return views?.result?.length > 1;
|
||||
return false;
|
||||
};
|
@ -2,3 +2,4 @@ export { getTableColumns } from './getTableColumns';
|
||||
export { getFKRelationships } from './getFKRelationships';
|
||||
export { getTablesListAsTree } from './getTablesListAsTree';
|
||||
export { getSupportedOperators } from './getSupportedOperators';
|
||||
export { getIsTableView } from './getIsTableView';
|
||||
|
@ -9,6 +9,7 @@ import {
|
||||
getFKRelationships,
|
||||
getTablesListAsTree,
|
||||
getSupportedOperators,
|
||||
getIsTableView,
|
||||
} from './introspection';
|
||||
import { getTableRows } from './query';
|
||||
import { postgresCapabilities } from '../common/capabilities';
|
||||
@ -78,6 +79,7 @@ export const cockroach: Database = {
|
||||
getTablesListAsTree,
|
||||
getSupportedOperators,
|
||||
getDatabaseSchemas: async () => Feature.NotImplemented,
|
||||
getIsTableView,
|
||||
},
|
||||
query: {
|
||||
getTableRows,
|
||||
|
@ -0,0 +1,30 @@
|
||||
import { runSQL } from '../../api';
|
||||
import { GetIsTableViewProps } from '../../types';
|
||||
import { CockroachDBTable } from '../index';
|
||||
|
||||
export const getIsTableView = async ({
|
||||
dataSourceName,
|
||||
table,
|
||||
httpClient,
|
||||
}: GetIsTableViewProps) => {
|
||||
const { schema, name } = table as CockroachDBTable;
|
||||
|
||||
const sql = `
|
||||
SELECT TABLE_NAME
|
||||
FROM information_schema.views
|
||||
WHERE TABLE_SCHEMA = '${schema}'
|
||||
AND TABLE_NAME = '${name}';`;
|
||||
|
||||
const views = await runSQL({
|
||||
source: {
|
||||
name: dataSourceName,
|
||||
kind: 'postgres',
|
||||
},
|
||||
sql: sql,
|
||||
readOnly: true,
|
||||
httpClient,
|
||||
});
|
||||
|
||||
if (Array.isArray(views?.result)) return views?.result?.length > 1;
|
||||
return false;
|
||||
};
|
@ -1,3 +1,4 @@
|
||||
export { getIsTableView } from './getIsTableView';
|
||||
export { getTableColumns } from './getTableColumns';
|
||||
export { getFKRelationships } from './getFKRelationships';
|
||||
export { getTablesListAsTree } from './getTablesListAsTree';
|
||||
|
@ -17,6 +17,7 @@ export const defaultDatabaseProps: Database = {
|
||||
getTablesListAsTree: async () => Feature.NotImplemented,
|
||||
getSupportedOperators: async () => Feature.NotImplemented,
|
||||
getDatabaseSchemas: async () => Feature.NotImplemented,
|
||||
getIsTableView: async () => Feature.NotImplemented,
|
||||
},
|
||||
query: {
|
||||
getTableRows: async () => Feature.NotImplemented,
|
||||
|
@ -32,6 +32,7 @@ export const gdc: Database = {
|
||||
getTablesListAsTree,
|
||||
getSupportedOperators,
|
||||
getDatabaseSchemas: async () => Feature.NotImplemented,
|
||||
getIsTableView: async () => Feature.NotImplemented,
|
||||
},
|
||||
query: {
|
||||
getTableRows,
|
||||
|
@ -23,6 +23,7 @@ import type {
|
||||
GetTablesListAsTreeProps,
|
||||
GetTrackableTablesProps,
|
||||
GetVersionProps,
|
||||
GetIsTableViewProps,
|
||||
// Property,
|
||||
IntrospectedTable,
|
||||
Operator,
|
||||
@ -135,6 +136,9 @@ export type Database = {
|
||||
getDatabaseSchemas: (
|
||||
props: GetDatabaseSchemaProps
|
||||
) => Promise<string[] | Feature.NotImplemented>;
|
||||
getIsTableView: (
|
||||
props: GetIsTableViewProps
|
||||
) => Promise<boolean | Feature.NotImplemented>;
|
||||
};
|
||||
query?: {
|
||||
getTableRows: (
|
||||
@ -550,4 +554,30 @@ export const DataSource = (httpClient: AxiosInstance) => ({
|
||||
|
||||
return database.modify;
|
||||
},
|
||||
getIsTableView: async ({
|
||||
dataSourceName,
|
||||
table,
|
||||
httpClient,
|
||||
}: {
|
||||
dataSourceName: string;
|
||||
table: Table;
|
||||
} & NetworkArgs) => {
|
||||
const database = await getDatabaseMethods({ dataSourceName, httpClient });
|
||||
|
||||
if (!database) return false;
|
||||
|
||||
const introspection = database.introspection;
|
||||
|
||||
if (!introspection) return false;
|
||||
|
||||
const isView = await introspection.getIsTableView({
|
||||
dataSourceName,
|
||||
httpClient,
|
||||
table,
|
||||
});
|
||||
|
||||
if (isView === Feature.NotImplemented) return false;
|
||||
|
||||
return isView;
|
||||
},
|
||||
});
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
getSupportedOperators,
|
||||
getTableColumns,
|
||||
getTablesListAsTree,
|
||||
getIsTableView,
|
||||
} from './introspection';
|
||||
import { getTableRows } from './query';
|
||||
|
||||
@ -81,6 +82,7 @@ export const mssql: Database = {
|
||||
getTablesListAsTree,
|
||||
getSupportedOperators,
|
||||
getDatabaseSchemas,
|
||||
getIsTableView,
|
||||
},
|
||||
modify: {
|
||||
defaultQueryRoot: async () => Feature.NotImplemented,
|
||||
|
@ -0,0 +1,30 @@
|
||||
import { runSQL } from '../../api';
|
||||
import { GetIsTableViewProps } from '../../types';
|
||||
import { MssqlTable } from '../index';
|
||||
|
||||
export const getIsTableView = async ({
|
||||
dataSourceName,
|
||||
table,
|
||||
httpClient,
|
||||
}: GetIsTableViewProps) => {
|
||||
const { schema, name } = table as MssqlTable;
|
||||
|
||||
const sql = `
|
||||
SELECT TABLE_NAME
|
||||
FROM information_schema.views
|
||||
WHERE TABLE_SCHEMA = '${schema}'
|
||||
AND TABLE_NAME = '${name}';`;
|
||||
|
||||
const views = await runSQL({
|
||||
source: {
|
||||
name: dataSourceName,
|
||||
kind: 'postgres',
|
||||
},
|
||||
sql: sql,
|
||||
readOnly: true,
|
||||
httpClient,
|
||||
});
|
||||
|
||||
if (Array.isArray(views?.result)) return views?.result?.length > 1;
|
||||
return false;
|
||||
};
|
@ -3,3 +3,4 @@ export { getFKRelationships } from './getFKRelationships';
|
||||
export { getTablesListAsTree } from './getTablesListAsTree';
|
||||
export { getSupportedOperators } from './getSupportedOperators';
|
||||
export { getDatabaseSchemas } from './getDatabaseSchemas';
|
||||
export { getIsTableView } from './getIsTableView';
|
||||
|
@ -20,6 +20,7 @@ export const mysql: Database = {
|
||||
getTablesListAsTree: async () => Feature.NotImplemented,
|
||||
getSupportedOperators: async () => Feature.NotImplemented,
|
||||
getDatabaseSchemas: async () => Feature.NotImplemented,
|
||||
getIsTableView: async () => Feature.NotImplemented,
|
||||
},
|
||||
query: {
|
||||
getTableRows: async () => Feature.NotImplemented,
|
||||
|
@ -16,6 +16,7 @@ import {
|
||||
getTableColumns,
|
||||
getTablesListAsTree,
|
||||
getTrackableTables,
|
||||
getIsTableView,
|
||||
} from './introspection';
|
||||
import { getTableRows } from './query';
|
||||
|
||||
@ -58,6 +59,7 @@ export const postgres: Database = {
|
||||
getTablesListAsTree,
|
||||
getSupportedOperators,
|
||||
getDatabaseSchemas,
|
||||
getIsTableView,
|
||||
},
|
||||
query: {
|
||||
getTableRows,
|
||||
|
@ -0,0 +1,30 @@
|
||||
import { runSQL } from '../../api';
|
||||
import { GetIsTableViewProps } from '../../types';
|
||||
import { PostgresTable } from '../index';
|
||||
|
||||
export const getIsTableView = async ({
|
||||
dataSourceName,
|
||||
table,
|
||||
httpClient,
|
||||
}: GetIsTableViewProps) => {
|
||||
const { schema, name } = table as PostgresTable;
|
||||
|
||||
const sql = `
|
||||
SELECT TABLE_NAME
|
||||
FROM information_schema.views
|
||||
WHERE TABLE_SCHEMA = '${schema}'
|
||||
AND TABLE_NAME = '${name}';`;
|
||||
|
||||
const views = await runSQL({
|
||||
source: {
|
||||
name: dataSourceName,
|
||||
kind: 'postgres',
|
||||
},
|
||||
sql: sql,
|
||||
readOnly: true,
|
||||
httpClient,
|
||||
});
|
||||
|
||||
if (Array.isArray(views?.result)) return views?.result?.length > 1;
|
||||
return false;
|
||||
};
|
@ -34,7 +34,7 @@ export const getTableColumns = async ({
|
||||
const { schema, name } = table as PostgresTable;
|
||||
|
||||
const sql = `
|
||||
SELECT
|
||||
SELECT
|
||||
column_name, data_type, is_nullable
|
||||
FROM
|
||||
information_schema.columns
|
||||
|
@ -5,3 +5,4 @@ export { getFKRelationships } from './getFKRelationships';
|
||||
export { getTablesListAsTree } from './getTablesListAsTree';
|
||||
export { getSupportedOperators } from './getSupportedOperators';
|
||||
export { getDatabaseSchemas } from './getDatabaseSchemas';
|
||||
export { getIsTableView } from './getIsTableView';
|
||||
|
@ -178,3 +178,8 @@ export type ChangeDatabaseSchemaProps = {
|
||||
dataSourceName: string;
|
||||
schemaName: string;
|
||||
} & NetworkArgs;
|
||||
export type GetIsTableViewProps = {
|
||||
dataSourceName: string;
|
||||
table: Table;
|
||||
httpClient: NetworkArgs['httpClient'];
|
||||
};
|
||||
|
@ -9,10 +9,27 @@ import { TableMachine } from './hooks';
|
||||
import { useDriverCapabilities } from '../../Data/hooks/useDriverCapabilities';
|
||||
import { Capabilities } from '@hasura/dc-api-types';
|
||||
import { getDriversSupportedQueryTypes } from './utils/getDriversSupportedQueryTypes';
|
||||
import { useIsTableView } from '../../Data/hooks/useIsTableView';
|
||||
|
||||
const queryType = ['insert', 'select', 'update', 'delete'] as const;
|
||||
type QueryType = (typeof queryType)[number];
|
||||
|
||||
const getIsColumnEditable = (
|
||||
roleName: string,
|
||||
isView: boolean | undefined,
|
||||
driverSupportedQueries: string[],
|
||||
permissionType: string
|
||||
) => {
|
||||
if (roleName === 'admin') return false;
|
||||
|
||||
if (isView) {
|
||||
return permissionType === 'select';
|
||||
}
|
||||
if (driverSupportedQueries.includes(permissionType)) return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
interface ViewPermissionsNoteProps {
|
||||
viewsSupported: boolean;
|
||||
supportedQueryTypes: QueryType[];
|
||||
@ -72,6 +89,8 @@ export const PermissionsTable: React.FC<PermissionsTableProps> = ({
|
||||
|
||||
const [state, send] = machine;
|
||||
|
||||
const { data: isView } = useIsTableView({ dataSourceName, table });
|
||||
|
||||
if (isLoading)
|
||||
return (
|
||||
<div>
|
||||
@ -128,9 +147,12 @@ export const PermissionsTable: React.FC<PermissionsTableProps> = ({
|
||||
{permissionTypes.map(({ permissionType, access }) => {
|
||||
// TODO: add checks to see what permissions are supported by each db
|
||||
|
||||
const isEditable =
|
||||
driverSupportedQueries.includes(permissionType) ||
|
||||
roleName !== 'admin';
|
||||
const isEditable = getIsColumnEditable(
|
||||
roleName,
|
||||
isView,
|
||||
driverSupportedQueries,
|
||||
permissionType
|
||||
);
|
||||
|
||||
if (isNewRole) {
|
||||
return (
|
||||
|
Loading…
Reference in New Issue
Block a user