Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ OBJS = src/backend/age.o \
src/backend/utils/load/ag_load_labels.o \
src/backend/utils/load/ag_load_edges.o \
src/backend/utils/load/age_load.o \
src/backend/utils/load/libcsv.o \
src/backend/utils/name_validation.o \
src/backend/utils/ag_guc.o

Expand Down
189 changes: 189 additions & 0 deletions regress/expected/age_load.out
Original file line number Diff line number Diff line change
Expand Up @@ -454,6 +454,195 @@ NOTICE: graph "agload_conversion" has been dropped

(1 row)

--
-- Test security and permissions
--
SELECT create_graph('agload_security');
NOTICE: graph "agload_security" has been created
create_graph
--------------

(1 row)

SELECT create_vlabel('agload_security', 'Person1');
NOTICE: VLabel "Person1" has been created
create_vlabel
---------------

(1 row)

SELECT create_vlabel('agload_security', 'Person2');
NOTICE: VLabel "Person2" has been created
create_vlabel
---------------

(1 row)

SELECT create_elabel('agload_security', 'SecEdge');
NOTICE: ELabel "SecEdge" has been created
create_elabel
---------------

(1 row)

--
-- Test 1: File read permission (pg_read_server_files role)
--
-- Create a user without pg_read_server_files role
CREATE USER load_test_user;
GRANT USAGE ON SCHEMA ag_catalog TO load_test_user;
-- This should fail because load_test_user doesn't have pg_read_server_files
SET ROLE load_test_user;
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
ERROR: permission denied to LOAD from a file
DETAIL: Only roles with privileges of the "pg_read_server_files" role may LOAD from a file.
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
ERROR: permission denied to LOAD from a file
DETAIL: Only roles with privileges of the "pg_read_server_files" role may LOAD from a file.
RESET ROLE;
-- Grant pg_read_server_files and try again - should fail on table permission now
GRANT pg_read_server_files TO load_test_user;
--
-- Test 2: Table INSERT permission (ACL_INSERT)
--
-- User has file read permission but no INSERT on the label table
SET ROLE load_test_user;
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
ERROR: permission denied for table Person1
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
ERROR: permission denied for table SecEdge
RESET ROLE;
-- Grant INSERT permission and try again - should succeed
GRANT USAGE ON SCHEMA agload_security TO load_test_user;
GRANT INSERT ON agload_security."Person1" TO load_test_user;
GRANT INSERT ON agload_security."SecEdge" TO load_test_user;
GRANT UPDATE ON SEQUENCE agload_security."Person1_id_seq" TO load_test_user;
GRANT UPDATE ON SEQUENCE agload_security."SecEdge_id_seq" TO load_test_user;
GRANT SELECT ON ag_catalog.ag_label TO load_test_user;
GRANT SELECT ON ag_catalog.ag_graph TO load_test_user;
SET ROLE load_test_user;
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
load_labels_from_file
-----------------------

(1 row)

SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
load_edges_from_file
----------------------

(1 row)

RESET ROLE;
-- Verify data was loaded
SELECT COUNT(*) FROM agload_security."Person1";
count
-------
6
(1 row)

SELECT COUNT(*) FROM agload_security."SecEdge";
count
-------
6
(1 row)

-- cleanup
DELETE FROM agload_security."Person1";
DELETE FROM agload_security."SecEdge";
--
-- Test 3: Row-Level Security (RLS)
--
-- Enable RLS on the label tables
ALTER TABLE agload_security."Person1" ENABLE ROW LEVEL SECURITY;
ALTER TABLE agload_security."SecEdge" ENABLE ROW LEVEL SECURITY;
-- Switch to load_test_user
SET ROLE load_test_user;
-- Loading should fail when RLS is enabled
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
ERROR: LOAD from file is not supported with row-level security
HINT: Use Cypher CREATE clause instead.
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
ERROR: LOAD from file is not supported with row-level security
HINT: Use Cypher CREATE clause instead.
RESET ROLE;
-- Disable RLS and try again - should succeed
ALTER TABLE agload_security."Person1" DISABLE ROW LEVEL SECURITY;
ALTER TABLE agload_security."SecEdge" DISABLE ROW LEVEL SECURITY;
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
load_labels_from_file
-----------------------

(1 row)

SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
load_edges_from_file
----------------------

(1 row)

-- Verify data was loaded
SELECT COUNT(*) FROM agload_security."Person1";
count
-------
6
(1 row)

SELECT COUNT(*) FROM agload_security."SecEdge";
count
-------
6
(1 row)

-- cleanup
DELETE FROM agload_security."Person1";
DELETE FROM agload_security."SecEdge";
--
-- Test 4: Constraint checking (CHECK constraint)
--
-- Add constraint on vertex properties - fail if bool property is false
ALTER TABLE agload_security."Person1" ADD CONSTRAINT check_bool_true
CHECK ((properties->>'"bool"')::boolean = true);
-- This should fail - constraint violation
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
ERROR: new row for relation "Person1" violates check constraint "check_bool_true"
DETAIL: Failing row contains (844424930131970, {"id": "2", "bool": "false", "__id__": 2, "string": "John", "num...).
-- Add constraint on edge properties - fail if bool property is false
ALTER TABLE agload_security."SecEdge" ADD CONSTRAINT check_bool_true
CHECK ((properties->>'"bool"')::boolean = true);
-- This should fail - some edges have bool = false
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
ERROR: new row for relation "SecEdge" violates check constraint "check_bool_true"
DETAIL: Failing row contains (1407374883553294, 844424930131969, 1125899906842625, {"bool": "false", "string": "John", "numeric": "-2"}).
-- cleanup
ALTER TABLE agload_security."Person1" DROP CONSTRAINT check_bool_true;
ALTER TABLE agload_security."SecEdge" DROP CONSTRAINT check_bool_true;
--
-- Cleanup
--
REVOKE ALL ON agload_security."Person1" FROM load_test_user;
REVOKE ALL ON agload_security."SecEdge" FROM load_test_user;
REVOKE ALL ON SEQUENCE agload_security."Person1_id_seq" FROM load_test_user;
REVOKE ALL ON SEQUENCE agload_security."SecEdge_id_seq" FROM load_test_user;
REVOKE ALL ON ag_catalog.ag_label FROM load_test_user;
REVOKE ALL ON ag_catalog.ag_graph FROM load_test_user;
REVOKE ALL ON SCHEMA agload_security FROM load_test_user;
REVOKE ALL ON SCHEMA ag_catalog FROM load_test_user;
REVOKE pg_read_server_files FROM load_test_user;
DROP USER load_test_user;
SELECT drop_graph('agload_security', true);
NOTICE: drop cascades to 5 other objects
DETAIL: drop cascades to table agload_security._ag_label_vertex
drop cascades to table agload_security._ag_label_edge
drop cascades to table agload_security."Person1"
drop cascades to table agload_security."Person2"
drop cascades to table agload_security."SecEdge"
NOTICE: graph "agload_security" has been dropped
drop_graph
------------

(1 row)

--
-- End
--
12 changes: 6 additions & 6 deletions regress/expected/index.out
Original file line number Diff line number Diff line change
Expand Up @@ -264,19 +264,19 @@ $$) as (n agtype);
(0 rows)

-- Verify that the incices are created on id columns
SELECT indexname, indexdef FROM pg_indexes WHERE schemaname= 'cypher_index';
SELECT indexname, indexdef FROM pg_indexes WHERE schemaname= 'cypher_index' ORDER BY 1;
indexname | indexdef
-----------------------------+------------------------------------------------------------------------------------------------
City_pkey | CREATE UNIQUE INDEX "City_pkey" ON cypher_index."City" USING btree (id)
Country_pkey | CREATE UNIQUE INDEX "Country_pkey" ON cypher_index."Country" USING btree (id)
_ag_label_edge_end_id_idx | CREATE INDEX _ag_label_edge_end_id_idx ON cypher_index._ag_label_edge USING btree (end_id)
_ag_label_edge_pkey | CREATE UNIQUE INDEX _ag_label_edge_pkey ON cypher_index._ag_label_edge USING btree (id)
_ag_label_edge_start_id_idx | CREATE INDEX _ag_label_edge_start_id_idx ON cypher_index._ag_label_edge USING btree (start_id)
_ag_label_edge_end_id_idx | CREATE INDEX _ag_label_edge_end_id_idx ON cypher_index._ag_label_edge USING btree (end_id)
_ag_label_vertex_pkey | CREATE UNIQUE INDEX _ag_label_vertex_pkey ON cypher_index._ag_label_vertex USING btree (id)
idx_pkey | CREATE UNIQUE INDEX idx_pkey ON cypher_index.idx USING btree (id)
cypher_index_idx_props_uq | CREATE UNIQUE INDEX cypher_index_idx_props_uq ON cypher_index.idx USING btree (properties)
Country_pkey | CREATE UNIQUE INDEX "Country_pkey" ON cypher_index."Country" USING btree (id)
has_city_start_id_idx | CREATE INDEX has_city_start_id_idx ON cypher_index.has_city USING btree (start_id)
has_city_end_id_idx | CREATE INDEX has_city_end_id_idx ON cypher_index.has_city USING btree (end_id)
City_pkey | CREATE UNIQUE INDEX "City_pkey" ON cypher_index."City" USING btree (id)
has_city_start_id_idx | CREATE INDEX has_city_start_id_idx ON cypher_index.has_city USING btree (start_id)
idx_pkey | CREATE UNIQUE INDEX idx_pkey ON cypher_index.idx USING btree (id)
(10 rows)

SET enable_mergejoin = ON;
Expand Down
125 changes: 125 additions & 0 deletions regress/sql/age_load.sql
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,131 @@ SELECT load_edges_from_file('agload_conversion', 'Edges1', '../../etc/passwd', t
--
SELECT drop_graph('agload_conversion', true);

--
-- Test security and permissions
--

SELECT create_graph('agload_security');
SELECT create_vlabel('agload_security', 'Person1');
SELECT create_vlabel('agload_security', 'Person2');
SELECT create_elabel('agload_security', 'SecEdge');

--
-- Test 1: File read permission (pg_read_server_files role)
--
-- Create a user without pg_read_server_files role
CREATE USER load_test_user;
GRANT USAGE ON SCHEMA ag_catalog TO load_test_user;

-- This should fail because load_test_user doesn't have pg_read_server_files
SET ROLE load_test_user;
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
RESET ROLE;

-- Grant pg_read_server_files and try again - should fail on table permission now
GRANT pg_read_server_files TO load_test_user;

--
-- Test 2: Table INSERT permission (ACL_INSERT)
--
-- User has file read permission but no INSERT on the label table
SET ROLE load_test_user;
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
RESET ROLE;

-- Grant INSERT permission and try again - should succeed
GRANT USAGE ON SCHEMA agload_security TO load_test_user;
GRANT INSERT ON agload_security."Person1" TO load_test_user;
GRANT INSERT ON agload_security."SecEdge" TO load_test_user;
GRANT UPDATE ON SEQUENCE agload_security."Person1_id_seq" TO load_test_user;
GRANT UPDATE ON SEQUENCE agload_security."SecEdge_id_seq" TO load_test_user;
GRANT SELECT ON ag_catalog.ag_label TO load_test_user;
GRANT SELECT ON ag_catalog.ag_graph TO load_test_user;

SET ROLE load_test_user;
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
RESET ROLE;

-- Verify data was loaded
SELECT COUNT(*) FROM agload_security."Person1";
SELECT COUNT(*) FROM agload_security."SecEdge";

-- cleanup
DELETE FROM agload_security."Person1";
DELETE FROM agload_security."SecEdge";

--
-- Test 3: Row-Level Security (RLS)
--

-- Enable RLS on the label tables
ALTER TABLE agload_security."Person1" ENABLE ROW LEVEL SECURITY;
ALTER TABLE agload_security."SecEdge" ENABLE ROW LEVEL SECURITY;

-- Switch to load_test_user
SET ROLE load_test_user;

-- Loading should fail when RLS is enabled
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');

RESET ROLE;

-- Disable RLS and try again - should succeed
ALTER TABLE agload_security."Person1" DISABLE ROW LEVEL SECURITY;
ALTER TABLE agload_security."SecEdge" DISABLE ROW LEVEL SECURITY;

SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');

-- Verify data was loaded
SELECT COUNT(*) FROM agload_security."Person1";
SELECT COUNT(*) FROM agload_security."SecEdge";

-- cleanup
DELETE FROM agload_security."Person1";
DELETE FROM agload_security."SecEdge";

--
-- Test 4: Constraint checking (CHECK constraint)
--

-- Add constraint on vertex properties - fail if bool property is false
ALTER TABLE agload_security."Person1" ADD CONSTRAINT check_bool_true
CHECK ((properties->>'"bool"')::boolean = true);

-- This should fail - constraint violation
SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);

-- Add constraint on edge properties - fail if bool property is false
ALTER TABLE agload_security."SecEdge" ADD CONSTRAINT check_bool_true
CHECK ((properties->>'"bool"')::boolean = true);

-- This should fail - some edges have bool = false
SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');

-- cleanup
ALTER TABLE agload_security."Person1" DROP CONSTRAINT check_bool_true;
ALTER TABLE agload_security."SecEdge" DROP CONSTRAINT check_bool_true;

--
-- Cleanup
--
REVOKE ALL ON agload_security."Person1" FROM load_test_user;
REVOKE ALL ON agload_security."SecEdge" FROM load_test_user;
REVOKE ALL ON SEQUENCE agload_security."Person1_id_seq" FROM load_test_user;
REVOKE ALL ON SEQUENCE agload_security."SecEdge_id_seq" FROM load_test_user;
REVOKE ALL ON ag_catalog.ag_label FROM load_test_user;
REVOKE ALL ON ag_catalog.ag_graph FROM load_test_user;
REVOKE ALL ON SCHEMA agload_security FROM load_test_user;
REVOKE ALL ON SCHEMA ag_catalog FROM load_test_user;
REVOKE pg_read_server_files FROM load_test_user;
DROP USER load_test_user;
SELECT drop_graph('agload_security', true);

--
-- End
--
2 changes: 1 addition & 1 deletion regress/sql/index.sql
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ SELECT * FROM cypher('cypher_index', $$
$$) as (n agtype);

-- Verify that the incices are created on id columns
SELECT indexname, indexdef FROM pg_indexes WHERE schemaname= 'cypher_index';
SELECT indexname, indexdef FROM pg_indexes WHERE schemaname= 'cypher_index' ORDER BY 1;

SET enable_mergejoin = ON;
SET enable_hashjoin = OFF;
Expand Down
Loading