Prevent incorrect usage of CNAME and DNAME record types
[regano.git] / db_functions.sql
blobff259dbefe936979cc021dfccf704a397ca80bf6
1 -- Regano database internal function definitions
2 --
3 -- Uses PostgreSQL extensions.
4 --
5 -- These functions are for internal use.
6 --
7 --  Regano is a domain registration system for OpenNIC TLDs written in
8 --  Perl.  This file is part of Regano.
9 --
10 --  Regano may be distributed under the same terms as Perl itself.  Of
11 --  particular importance, note that while regano is distributed in the
12 --  hope that it will be useful, there is NO WARRANTY OF ANY KIND
13 --  WHATSOEVER WHETHER EXPLICIT OR IMPLIED.
16 -- The type definitions in db_types.sql must already be installed.
17 -- The table definitions in db_tables.sql must already be installed.
19 CREATE OR REPLACE FUNCTION regano.config_set
20         (key text, value bigint)
21         RETURNS void AS $$
22 BEGIN
23     PERFORM * FROM regano.config WHERE regano.config.key = config_set.key;
24     IF FOUND THEN
25         UPDATE regano.config SET number = value
26             WHERE regano.config.key = config_set.key;
27     ELSE
28         INSERT INTO regano.config (key, number) VALUES (key, value);
29     END IF;
30 END;
31 $$ LANGUAGE plpgsql VOLATILE STRICT SECURITY INVOKER;
32 ALTER FUNCTION regano.config_set (text, bigint)
33         OWNER TO regano;
35 CREATE OR REPLACE FUNCTION regano.config_set
36         (key text, value interval)
37         RETURNS void AS $$
38 BEGIN
39     PERFORM * FROM regano.config WHERE regano.config.key = config_set.key;
40     IF FOUND THEN
41         UPDATE regano.config SET interval = value
42             WHERE regano.config.key = config_set.key;
43     ELSE
44         INSERT INTO regano.config (key, interval) VALUES (key, value);
45     END IF;
46 END;
47 $$ LANGUAGE plpgsql VOLATILE STRICT SECURITY INVOKER;
48 ALTER FUNCTION regano.config_set (text, interval)
49         OWNER TO regano;
51 CREATE OR REPLACE FUNCTION regano.config_set
52         (key text, value text)
53         RETURNS void AS $$
54 BEGIN
55     PERFORM * FROM regano.config WHERE regano.config.key = config_set.key;
56     IF FOUND THEN
57         UPDATE regano.config SET text = value
58             WHERE regano.config.key = config_set.key;
59     ELSE
60         INSERT INTO regano.config (key, text) VALUES (key, value);
61     END IF;
62 END;
63 $$ LANGUAGE plpgsql VOLATILE STRICT SECURITY INVOKER;
64 ALTER FUNCTION regano.config_set (text, text)
65         OWNER TO regano;
68 CREATE OR REPLACE FUNCTION regano.config_get (key text)
69         RETURNS regano.config AS $$
70 SELECT * FROM regano.config WHERE key = $1
71 $$ LANGUAGE SQL STABLE STRICT SECURITY INVOKER;
72 ALTER FUNCTION regano.config_get (text)
73         OWNER TO regano;
75 CREATE OR REPLACE FUNCTION regano.advance_timestamp_by_config_item
76         (timestamp with time zone, regano.config)
77         RETURNS timestamp with time zone AS $$
78 SELECT $1 + $2.interval
79 $$ LANGUAGE SQL IMMUTABLE STRICT SECURITY INVOKER;
80 ALTER FUNCTION regano.advance_timestamp_by_config_item
81         (timestamp with time zone, regano.config)
82         OWNER TO regano;
83 CREATE OPERATOR + (
84     leftarg = timestamp with time zone,
85     rightarg = regano.config,
86     procedure = regano.advance_timestamp_by_config_item
90 CREATE OR REPLACE FUNCTION regano.contact_next_id (bigint)
91         RETURNS integer AS $$
92 SELECT COALESCE(MAX(id), 0) + 1
93     FROM regano.contacts WHERE owner_id = $1;
94 $$ LANGUAGE SQL STABLE STRICT SECURITY INVOKER;
95 ALTER FUNCTION regano.contact_next_id (bigint)
96         OWNER TO regano;
98 CREATE OR REPLACE FUNCTION regano.username (regano.sessions)
99         RETURNS text AS $$
100 SELECT username FROM regano.users WHERE id = $1.user_id;
101 $$ LANGUAGE SQL STABLE STRICT SECURITY INVOKER;
102 ALTER FUNCTION regano.username (regano.sessions)
103         OWNER TO regano;
104 CREATE OR REPLACE FUNCTION regano.username (session_id uuid)
105         RETURNS text AS $$
106 SELECT username
107     FROM regano.sessions JOIN regano.users
108         ON (regano.sessions.user_id = regano.users.id)
109     WHERE regano.sessions.id = $1;
110 $$ LANGUAGE SQL STABLE STRICT SECURITY INVOKER;
111 ALTER FUNCTION regano.username (uuid)
112         OWNER TO regano;
114 CREATE OR REPLACE FUNCTION regano.session_user_id (uuid) RETURNS bigint AS $$
115 SELECT user_id FROM regano.sessions WHERE id = $1
116 $$ LANGUAGE SQL STABLE STRICT SECURITY INVOKER;
117 ALTER FUNCTION regano.session_user_id (uuid)
118         OWNER TO regano;
120 CREATE OR REPLACE FUNCTION regano.zone_verify_access
121         (session_id uuid, zone_name regano.dns_fqdn, action text)
122         RETURNS regano.domains AS $$
123 DECLARE
124     user_id             CONSTANT bigint NOT NULL
125                             := regano.session_user_id(session_id);
127     primary_label       regano.dns_label;
128     tail                regano.dns_fqdn;
130     domain              regano.domains%ROWTYPE;
131 BEGIN
132     primary_label := substring(zone_name from '^([^.]+)[.]');
133     tail := substring(zone_name from '^[^.]+([.].+[.])$');
135     SELECT * INTO STRICT domain
136         FROM regano.domains
137         WHERE (lower(primary_label) = lower(domain_name))
138             AND (lower(tail) = lower(domain_tail));
140     IF user_id <> domain.owner_id THEN
141         RAISE EXCEPTION
142         'attempt made to modify zone (%) not belonging to current user (%): %',
143             zone_name, regano.username(session_id), action;
144     END IF;
146     RETURN domain;
148 $$ LANGUAGE plpgsql STABLE STRICT SECURITY INVOKER;
149 ALTER FUNCTION regano.zone_verify_access (uuid, regano.dns_fqdn, text)
150         OWNER TO regano;
152 CREATE OR REPLACE FUNCTION regano.zone_verify_records
153         (domain regano.domains)
154         RETURNS void AS $$
155 DECLARE
156     count               bigint;
157 BEGIN
158     -- CNAME records must have unique labels among all records         [RFC2181]
159     WITH aliases AS (SELECT domain_id, seq_no, name
160                         FROM regano.domain_records
161                         WHERE type = 'CNAME' AND domain_id = domain.id)
162         SELECT COUNT(*) INTO STRICT count
163             FROM aliases a INNER JOIN regano.domain_records r
164                 USING (domain_id, name)
165             WHERE a.seq_no <> r.seq_no;
166     IF count > 0 THEN
167         RAISE EXCEPTION 'CNAME and other data at same point in DNS zone';
168     END IF;
170     -- DNAME records must have no descendants                          [RFC2672]
171     WITH aliases AS (SELECT domain_id, seq_no, name
172                         FROM regano.domain_records
173                         WHERE type = 'DNAME' AND domain_id = domain.id)
174         SELECT COUNT(*) INTO STRICT count
175             FROM aliases a INNER JOIN regano.domain_records r USING (domain_id)
176             WHERE a.seq_no <> r.seq_no AND r.name LIKE '%.'||a.name;
177     IF count > 0 THEN
178         RAISE EXCEPTION 'other data found below DNAME';
179     END IF;
181 $$ LANGUAGE plpgsql STABLE STRICT SECURITY INVOKER;
182 ALTER FUNCTION regano.zone_verify_records (regano.domains)
183         OWNER TO regano;
185 CREATE OR REPLACE FUNCTION regano.zone_next_seq_no (bigint)
186         RETURNS bigint AS $$
187 SELECT COALESCE(MAX(seq_no), 0) + 1
188     FROM regano.domain_records WHERE domain_id = $1;
189 $$ LANGUAGE SQL STABLE STRICT SECURITY INVOKER;
190 ALTER FUNCTION regano.zone_next_seq_no (bigint)
191         OWNER TO regano;
193 CREATE OR REPLACE FUNCTION regano.canonicalize_record_name
194         (input regano.dns_name, zone_name regano.dns_fqdn)
195         RETURNS regano.dns_name AS $$
196 SELECT CASE WHEN lower($1) = lower($2) THEN regano.dns_name '@'
197             WHEN char_length($1) > (1+char_length($2))
198                  AND (lower($1) LIKE lower('%.' || $2))
199                  THEN CAST(substring($1 from 1
200                                      for (char_length($1) - char_length($2) - 1))
201                            AS regano.dns_name)
202             ELSE $1
203        END
204 $$ LANGUAGE SQL IMMUTABLE STRICT SECURITY INVOKER;
205 ALTER FUNCTION regano.canonicalize_record_name
206         (regano.dns_name, regano.dns_fqdn)
207         OWNER TO regano;