@@ -454,6 +454,195 @@ NOTICE: graph "agload_conversion" has been dropped
454454
455455(1 row)
456456
457+ --
458+ -- Test security and permissions
459+ --
460+ SELECT create_graph('agload_security');
461+ NOTICE: graph "agload_security" has been created
462+ create_graph
463+ --------------
464+
465+ (1 row)
466+
467+ SELECT create_vlabel('agload_security', 'Person1');
468+ NOTICE: VLabel "Person1" has been created
469+ create_vlabel
470+ ---------------
471+
472+ (1 row)
473+
474+ SELECT create_vlabel('agload_security', 'Person2');
475+ NOTICE: VLabel "Person2" has been created
476+ create_vlabel
477+ ---------------
478+
479+ (1 row)
480+
481+ SELECT create_elabel('agload_security', 'SecEdge');
482+ NOTICE: ELabel "SecEdge" has been created
483+ create_elabel
484+ ---------------
485+
486+ (1 row)
487+
488+ --
489+ -- Test 1: File read permission (pg_read_server_files role)
490+ --
491+ -- Create a user without pg_read_server_files role
492+ CREATE USER load_test_user;
493+ GRANT USAGE ON SCHEMA ag_catalog TO load_test_user;
494+ -- This should fail because load_test_user doesn't have pg_read_server_files
495+ SET ROLE load_test_user;
496+ SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
497+ ERROR: permission denied to LOAD from a file
498+ DETAIL: Only roles with privileges of the "pg_read_server_files" role may LOAD from a file.
499+ SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
500+ ERROR: permission denied to LOAD from a file
501+ DETAIL: Only roles with privileges of the "pg_read_server_files" role may LOAD from a file.
502+ RESET ROLE;
503+ -- Grant pg_read_server_files and try again - should fail on table permission now
504+ GRANT pg_read_server_files TO load_test_user;
505+ --
506+ -- Test 2: Table INSERT permission (ACL_INSERT)
507+ --
508+ -- User has file read permission but no INSERT on the label table
509+ SET ROLE load_test_user;
510+ SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
511+ ERROR: permission denied for table Person1
512+ SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
513+ ERROR: permission denied for table SecEdge
514+ RESET ROLE;
515+ -- Grant INSERT permission and try again - should succeed
516+ GRANT USAGE ON SCHEMA agload_security TO load_test_user;
517+ GRANT INSERT ON agload_security."Person1" TO load_test_user;
518+ GRANT INSERT ON agload_security."SecEdge" TO load_test_user;
519+ GRANT UPDATE ON SEQUENCE agload_security."Person1_id_seq" TO load_test_user;
520+ GRANT UPDATE ON SEQUENCE agload_security."SecEdge_id_seq" TO load_test_user;
521+ GRANT SELECT ON ag_catalog.ag_label TO load_test_user;
522+ GRANT SELECT ON ag_catalog.ag_graph TO load_test_user;
523+ SET ROLE load_test_user;
524+ SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
525+ load_labels_from_file
526+ -----------------------
527+
528+ (1 row)
529+
530+ SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
531+ load_edges_from_file
532+ ----------------------
533+
534+ (1 row)
535+
536+ RESET ROLE;
537+ -- Verify data was loaded
538+ SELECT COUNT(*) FROM agload_security."Person1";
539+ count
540+ -------
541+ 6
542+ (1 row)
543+
544+ SELECT COUNT(*) FROM agload_security."SecEdge";
545+ count
546+ -------
547+ 6
548+ (1 row)
549+
550+ -- cleanup
551+ DELETE FROM agload_security."Person1";
552+ DELETE FROM agload_security."SecEdge";
553+ --
554+ -- Test 3: Row-Level Security (RLS)
555+ --
556+ -- Enable RLS on the label tables
557+ ALTER TABLE agload_security."Person1" ENABLE ROW LEVEL SECURITY;
558+ ALTER TABLE agload_security."SecEdge" ENABLE ROW LEVEL SECURITY;
559+ -- Switch to load_test_user
560+ SET ROLE load_test_user;
561+ -- Loading should fail when RLS is enabled
562+ SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
563+ ERROR: LOAD from file is not supported with row-level security
564+ HINT: Use Cypher CREATE clause instead.
565+ SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
566+ ERROR: LOAD from file is not supported with row-level security
567+ HINT: Use Cypher CREATE clause instead.
568+ RESET ROLE;
569+ -- Disable RLS and try again - should succeed
570+ ALTER TABLE agload_security."Person1" DISABLE ROW LEVEL SECURITY;
571+ ALTER TABLE agload_security."SecEdge" DISABLE ROW LEVEL SECURITY;
572+ SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
573+ load_labels_from_file
574+ -----------------------
575+
576+ (1 row)
577+
578+ SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
579+ load_edges_from_file
580+ ----------------------
581+
582+ (1 row)
583+
584+ -- Verify data was loaded
585+ SELECT COUNT(*) FROM agload_security."Person1";
586+ count
587+ -------
588+ 6
589+ (1 row)
590+
591+ SELECT COUNT(*) FROM agload_security."SecEdge";
592+ count
593+ -------
594+ 6
595+ (1 row)
596+
597+ -- cleanup
598+ DELETE FROM agload_security."Person1";
599+ DELETE FROM agload_security."SecEdge";
600+ --
601+ -- Test 4: Constraint checking (CHECK constraint)
602+ --
603+ -- Add constraint on vertex properties - fail if bool property is false
604+ ALTER TABLE agload_security."Person1" ADD CONSTRAINT check_bool_true
605+ CHECK ((properties->>'"bool"')::boolean = true);
606+ -- This should fail - constraint violation
607+ SELECT load_labels_from_file('agload_security', 'Person1', 'age_load/conversion_vertices.csv', true);
608+ ERROR: new row for relation "Person1" violates check constraint "check_bool_true"
609+ DETAIL: Failing row contains (844424930131970, {"id": "2", "bool": "false", "__id__": 2, "string": "John", "num...).
610+ -- Add constraint on edge properties - fail if bool property is false
611+ ALTER TABLE agload_security."SecEdge" ADD CONSTRAINT check_bool_true
612+ CHECK ((properties->>'"bool"')::boolean = true);
613+ -- This should fail - some edges have bool = false
614+ SELECT load_edges_from_file('agload_security', 'SecEdge', 'age_load/conversion_edges.csv');
615+ ERROR: new row for relation "SecEdge" violates check constraint "check_bool_true"
616+ DETAIL: Failing row contains (1407374883553294, 844424930131969, 1125899906842625, {"bool": "false", "string": "John", "numeric": "-2"}).
617+ -- cleanup
618+ ALTER TABLE agload_security."Person1" DROP CONSTRAINT check_bool_true;
619+ ALTER TABLE agload_security."SecEdge" DROP CONSTRAINT check_bool_true;
620+ --
621+ -- Cleanup
622+ --
623+ REVOKE ALL ON agload_security."Person1" FROM load_test_user;
624+ REVOKE ALL ON agload_security."SecEdge" FROM load_test_user;
625+ REVOKE ALL ON SEQUENCE agload_security."Person1_id_seq" FROM load_test_user;
626+ REVOKE ALL ON SEQUENCE agload_security."SecEdge_id_seq" FROM load_test_user;
627+ REVOKE ALL ON ag_catalog.ag_label FROM load_test_user;
628+ REVOKE ALL ON ag_catalog.ag_graph FROM load_test_user;
629+ REVOKE ALL ON SCHEMA agload_security FROM load_test_user;
630+ REVOKE ALL ON SCHEMA ag_catalog FROM load_test_user;
631+ REVOKE pg_read_server_files FROM load_test_user;
632+ DROP USER load_test_user;
633+ SELECT drop_graph('agload_security', true);
634+ NOTICE: drop cascades to 5 other objects
635+ DETAIL: drop cascades to table agload_security._ag_label_vertex
636+ drop cascades to table agload_security._ag_label_edge
637+ drop cascades to table agload_security."Person1"
638+ drop cascades to table agload_security."Person2"
639+ drop cascades to table agload_security."SecEdge"
640+ NOTICE: graph "agload_security" has been dropped
641+ drop_graph
642+ ------------
643+
644+ (1 row)
645+
457646--
458647-- End
459648--
0 commit comments