Skip to content

Commit 47f791d

Browse files
committed
Initial commit
0 parents  commit 47f791d

File tree

7 files changed

+324
-0
lines changed

7 files changed

+324
-0
lines changed

CHANGELOG

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Version 0.3 (beta), released 2015-01-22
2+
- initial release
3+

LICENSE

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Copyright (c) 2012-2015, BRGM
2+
3+
permission to use, copy, modify, and distribute this software and its
4+
documentation for any purpose, without fee, and without a written agreement
5+
is hereby granted, provided that the above copyright notice and this paragraph
6+
and the following two paragraphs appear in all copies.
7+
8+
IN NO EVENT SHALL THE BRGM BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT,
9+
SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS,
10+
ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
11+
THE MAGISTRAT DER STADT WIEN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
12+
DAMAGE.
13+
14+
THE BRGM SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED
15+
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16+
PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE
17+
BRGM HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
18+
OR MODIFICATIONS.

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#MODULES = external_file
2+
EXTENSION = external_file
3+
DATA = external_file--0.2.sql
4+
DOCS = README.external_file
5+
6+
PG_CONFIG = pg_config
7+
PGXS := $(shell $(PG_CONFIG) --pgxs)
8+
include $(PGXS)

README.external_file

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
External file access extension
2+
==============================
3+
4+
Allow access to "external files" from PostgreSQL server file systems.
5+
This extension is only a "secure version" of the server side lo_* functions.
6+
7+
1. Installation requirements
8+
============================
9+
PostgreSQL 9.1 or better are required.
10+
User with PostgreSQL superuser role for creating extension.
11+
12+
2. Installation
13+
===============
14+
external_file has been written as a PostgreSQL extension and uses the Extension
15+
Building Infrastructure "PGXS".
16+
17+
You will need PostgreSQL headers and PGXS installed (if your PostgreSQL was
18+
installed with packages, install the development package).
19+
20+
Get/Unpack the source code in a fresh directory Then the software installation
21+
should be as simple as
22+
23+
$ make (In these version do nothing)
24+
$ make install
25+
26+
To install the extension in a database, connect as superuser and
27+
28+
CREATE EXTENSION external_file;
29+
30+
By default all objects of the extension are created in the external_file schema.
31+
If you want to change the schema name you must edit the external_file.control
32+
file. Note that this schema must not be writable by normal user to not allow
33+
bypassing of the search path set with the security definer.
34+
35+
36+
When using schema with extension, it's better to include this schema in the
37+
default search_path. For example:
38+
39+
ALTER DATABASE <mydb> SET search_path="$user",public,external_file;
40+
41+
Also you can restrict USAGE grant on external_file schema to specific user and
42+
change the default search path at user level too.
43+
44+
GRANT USAGE ON SCHEMA external_file TO <username>;
45+
46+
Please refer to the PostgreSQL documentation for more information.
47+
48+
49+
3. Usage
50+
========
51+
External file are accessed using two values, an alias for the path of the
52+
directory where the file is, and the file name.
53+
54+
So, first, alias must be defined for the path. This definition is performed
55+
using the "directories" table. For security reason, only superuser can insert,
56+
update, delete directory definition. It's possible, with GRANT command, to
57+
change this but it's NOT recommended.
58+
59+
Example:
60+
61+
INSERT INTO directories(directory_name,directory_path) VALUES ('temporary','/tmp/');
62+
63+
ATTENTION:
64+
* the path must use the terminal separator!
65+
* the system user running PostgreSQL server (generally postgres) must have the
66+
system rights to read and/or write files
67+
* the filename don't include any / or \ character for security reason
68+
69+
Second, rights for user and/or role are defined using the "directory_access"
70+
table.
71+
72+
Example:
73+
74+
INSERT INTO directory_roles(directory_name,directory_role,directory_read,directory_write) VALUES ('temporary','a_role',true,false);
75+
76+
Now standard user can use external files.
77+
78+
Example:
79+
80+
-- Store a new external file blahblah.txt into the directory
81+
SELECT writeEfile('\x48656c6c6f2c0a0a596f75206172652072656164696e67206120746578742066696c652e0a0a526567617264732c0a', ('temporary', 'blahblah.txt'));
82+
83+
ls -la /tmp/blahblah.txt
84+
-rw-r--r-- 1 postgres postgres 47 janv. 22 19:16 /tmp/blahblah.txt
85+
86+
-- Create a table taht will use external files
87+
CREATE TABLE efile_test ( id smallint primary key, the_file efile);
88+
-- Insert a row to access the external file called blahblah.txt
89+
INSERT INTO efile_test VALUES (1,('temporary','blahblah.txt'));
90+
-- Assuming user has right to read, and the file exists
91+
SELECT id, readefile(the_file) FROM efile_test;
92+
-- Make a physical copy of the external file assuming user has right to read AND write
93+
SELECT copyefile(('temporary','blahblah.txt'),('temporary','copy_blahblah.txt'));
94+
INSERT INTO efile_test VALUES (2,('temporary','copy_blahblah.txt'));
95+
96+
ls /tmp/*blahblah.txt
97+
-rw-r--r-- 1 postgres postgres 47 janv. 22 19:16 /tmp/blahblah.txt
98+
-rw-r--r-- 1 postgres postgres 47 janv. 22 19:24 /tmp/copy_blahblah.txt
99+
100+
file=# SELECT id, readefile(the_file) FROM efile_test;
101+
id | readefile
102+
----+--------------------------------------------------------------------------------------------------
103+
1 | \x48656c6c6f2c0a0a596f75206172652072656164696e67206120746578742066696c652e0a0a526567617264732c0a
104+
2 | \x48656c6c6f2c0a0a596f75206172652072656164696e67206120746578742066696c652e0a0a526567617264732c0a
105+
(2 lines)
106+
107+
108+
4. Function reference
109+
=====================
110+
111+
readEfile(e_file in efile) returns bytea
112+
copy the external file into a bytea.
113+
Error will be generated if something wrong.
114+
115+
writeEfile(buffer in bytea, e_file in efile) returns void
116+
copy a bytea into a external file.
117+
Error will be generated if something wrong.
118+
119+
copyEfile(src in efile, dest in efile) returns void
120+
duplicate file defined by src into file dest
121+
Error will be generated if something wrong.
122+
123+
getEfilePath(e_file efile, need_read in boolean, need_write in boolean) returns text
124+
giving an efile and booleans, one for read and one for write need, return the
125+
full path for the file, otherwise an error is generated
126+
useful to check if session user has access to this external file
127+
128+
5. License
129+
==========
130+
Author Dominique Legendre
131+
Copyright (c) 2012-2015 Brgm - All rights reserved.
132+

TODO

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
This is a list of things that should be done.
2+
Help is welcome!
3+
4+
- Support to remove external files
5+
CREATE OR REPLACE FUNCTION removeEfile(e_file efile) RETURNS void ...

external_file--0.2.sql

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
-- External files extension for PostgreSQL
2+
-- Author Dominique Legendre
3+
-- Copyright (c) 2012-2015 Brgm - All rights reserved.
4+
5+
-- complain if script is sourced in psql, rather than via CREATE EXTENSION
6+
\echo Use "CREATE EXTENSION external_file" to load this file. \quit
7+
8+
9+
CREATE TABLE directories (
10+
directory_name name NOT NULL PRIMARY KEY,
11+
directory_path text NOT NULL
12+
);
13+
14+
REVOKE ALL ON directories FROM PUBLIC;
15+
GRANT SELECT ON directories TO PUBLIC;
16+
17+
CREATE TABLE directory_roles (
18+
directory_name name REFERENCES directories(directory_name) ON DELETE CASCADE ON UPDATE CASCADE,
19+
directory_role name,
20+
directory_read boolean NOT NULL,
21+
directory_write boolean NOT NULL,
22+
PRIMARY KEY (directory_name,directory_role)
23+
);
24+
25+
REVOKE ALL ON directory_roles FROM PUBLIC;
26+
GRANT SELECT ON directory_roles TO PUBLIC;
27+
28+
-- Include tables into pg_dump
29+
SELECT pg_catalog.pg_extension_config_dump('directories', '');
30+
SELECT pg_catalog.pg_extension_config_dump('directory_roles', '');
31+
32+
33+
CREATE TYPE efile AS (
34+
directory name,
35+
filename varchar(256)
36+
);
37+
38+
REVOKE ALL ON TYPE efile FROM PUBLIC;
39+
GRANT USAGE ON TYPE efile TO PUBLIC;
40+
41+
42+
CREATE OR REPLACE FUNCTION getEfilePath(e_file efile, need_read boolean, need_write boolean)
43+
RETURNS text
44+
AS $$
45+
DECLARE
46+
p_path text;
47+
r record;
48+
read_enable boolean := false;
49+
write_enable boolean := false;
50+
BEGIN
51+
IF coalesce(e_file.filename,'')='' THEN
52+
RAISE EXCEPTION 'Filename is empty.';
53+
END IF;
54+
IF e_file.filename ~ '(\\|/)' THEN
55+
RAISE EXCEPTION '/ or \ are forbiden inside filename';
56+
END IF;
57+
SELECT directory_path INTO p_path FROM directories WHERE directory_name= e_file.directory;
58+
IF NOT FOUND THEN
59+
RAISE EXCEPTION 'Directory % don''t exist.',e_file.directory;
60+
END IF;
61+
FOR r IN
62+
(SELECT directory_role,directory_read,directory_write FROM directory_roles WHERE directory_name= e_file.directory) LOOP
63+
IF pg_has_role(session_user,r.directory_role,'USAGE') THEN
64+
IF r.directory_read THEN
65+
read_enable := true;
66+
END IF;
67+
IF r.directory_write THEN
68+
write_enable := true;
69+
END IF;
70+
END IF;
71+
END LOOP;
72+
IF (need_read AND NOT read_enable) OR (need_write AND NOT write_enable) THEN
73+
RAISE EXCEPTION 'Missing right for this directory: %' ,e_file.directory;
74+
END IF;
75+
p_path := p_path|| e_file.filename;
76+
RETURN p_path;
77+
END;
78+
$$
79+
LANGUAGE PLPGSQL STABLE SECURITY DEFINER SET search_path = @extschema@, pg_temp;
80+
81+
82+
CREATE OR REPLACE FUNCTION writeEfile(buffer bytea, e_file efile)
83+
RETURNS void
84+
AS $$
85+
DECLARE
86+
l_oid oid;
87+
lfd integer;
88+
lsize integer;
89+
BEGIN
90+
l_oid := lo_create(0);
91+
lfd := lo_open(l_oid,131072); --0x00020000 write mode
92+
lsize := lowrite(lfd,buffer);
93+
PERFORM lo_close(lfd);
94+
PERFORM lo_export(l_oid,getEfilePath(e_file,false,true));
95+
PERFORM lo_unlink(l_oid);
96+
END;
97+
$$ LANGUAGE PLPGSQL SECURITY DEFINER SET search_path = @extschema@, pg_temp;
98+
99+
100+
CREATE OR REPLACE FUNCTION readEfile(e_file efile, p_result OUT bytea)
101+
AS $$
102+
DECLARE
103+
l_oid oid;
104+
r record;
105+
BEGIN
106+
p_result := '';
107+
SELECT lo_import(getEfilePath(e_file,true,false)) INTO l_oid;
108+
FOR r IN ( SELECT data
109+
FROM pg_largeobject
110+
WHERE loid = l_oid
111+
ORDER BY pageno ) LOOP
112+
p_result = p_result || r.data;
113+
END LOOP;
114+
PERFORM lo_unlink(l_oid);
115+
END;
116+
$$
117+
LANGUAGE PLPGSQL SECURITY DEFINER SET search_path = @extschema@, pg_temp;
118+
119+
120+
CREATE OR REPLACE FUNCTION copyEfile(src efile, dest efile)
121+
RETURNS void
122+
AS $$
123+
DECLARE
124+
l_oid oid;
125+
BEGIN
126+
SELECT lo_import(getEfilePath(src,true,false)) INTO l_oid;
127+
PERFORM lo_export(l_oid,getEfilePath(dest,false,true));
128+
PERFORM lo_unlink(l_oid);
129+
END;
130+
$$
131+
LANGUAGE PLPGSQL SECURITY DEFINER SET search_path = @extschema@, pg_temp;
132+
133+
CREATE OR REPLACE FUNCTION efile_check_role()
134+
RETURNS trigger
135+
AS $$
136+
BEGIN
137+
PERFORM * from pg_roles where rolname = NEW.directory_role;
138+
IF NOT FOUND THEN
139+
RAISE EXCEPTION 'role % must exists', NEW.directory_role;
140+
RETURN NULL;
141+
END IF;
142+
RETURN NEW;
143+
END;
144+
$$
145+
LANGUAGE PLPGSQL;
146+
147+
CREATE TRIGGER trg_efile_check_role
148+
BEFORE UPDATE OR INSERT ON directory_roles
149+
FOR EACH ROW
150+
EXECUTE PROCEDURE efile_check_role();
151+
152+

external_file.control

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
comment = 'functions to read or write files from postgresql server filesystems'
2+
default_version = '0.2'
3+
encoding = 'utf8'
4+
relocatable = false
5+
superuser = true
6+
schema = external_file

0 commit comments

Comments
 (0)