]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/acl.c
has_table_privilege functions from Joe Conway (with some kibitzing from
[postgresql] / src / backend / utils / adt / acl.c
1 /*-------------------------------------------------------------------------
2  *
3  * acl.c
4  *        Basic access control list data structures manipulation routines.
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/utils/adt/acl.c,v 1.64 2001/06/14 01:09:22 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16
17 #include <ctype.h>
18
19 #include "access/heapam.h"
20 #include "catalog/catalog.h"
21 #include "catalog/pg_shadow.h"
22 #include "catalog/pg_type.h"
23 #include "lib/stringinfo.h"
24 #include "miscadmin.h"
25 #include "utils/acl.h"
26 #include "utils/builtins.h"
27 #include "utils/memutils.h"
28 #include "utils/lsyscache.h"
29 #include "utils/syscache.h"
30
31
32 #define ACL_IDTYPE_GID_KEYWORD  "group"
33 #define ACL_IDTYPE_UID_KEYWORD  "user"
34
35 static const char *getid(const char *s, char *n);
36 static bool aclitemeq(const AclItem *a1, const AclItem *a2);
37 static bool aclitemgt(const AclItem *a1, const AclItem *a2);
38
39 static AclMode convert_priv_string(text *priv_type_text);
40 static bool has_table_privilege_cname_cname(char *username, char *relname,
41                                                                                         text *priv_type_text);
42 static bool has_table_privilege_cname_id(char *username, Oid reloid,
43                                                                                  text *priv_type_text);
44 static bool has_table_privilege_id_cname(int32 usesysid, char *relname,
45                                                                                  text *priv_type_text);
46
47
48 /*
49  * getid
50  *              Consumes the first alphanumeric string (identifier) found in string
51  *              's', ignoring any leading white space.  If it finds a double quote
52  *              it returns the word inside the quotes.
53  *
54  * RETURNS:
55  *              the string position in 's' that points to the next non-space character
56  *              in 's', after any quotes.  Also:
57  *              - loads the identifier into 'name'.  (If no identifier is found, 'name'
58  *                contains an empty string.)  name must be NAMEDATALEN bytes.
59  */
60 static const char *
61 getid(const char *s, char *n)
62 {
63         unsigned        len;
64         const char *id;
65         int                     in_quotes = 0;
66
67         Assert(s && n);
68
69         while (isspace((unsigned char) *s))
70                 ++s;
71
72         if (*s == '"')
73         {
74                 in_quotes = 1;
75                 s++;
76         }
77
78         for (id = s, len = 0;
79                  isalnum((unsigned char) *s) || *s == '_' || in_quotes;
80                  ++len, ++s)
81         {
82                 if (in_quotes && *s == '"')
83                 {
84                         len--;
85                         in_quotes = 0;
86                 }
87         }
88         if (len >= NAMEDATALEN)
89                 elog(ERROR, "getid: identifier must be <%d characters",
90                          NAMEDATALEN);
91         if (len > 0)
92                 memmove(n, id, len);
93         n[len] = '\0';
94         while (isspace((unsigned char) *s))
95                 ++s;
96         return s;
97 }
98
99 /*
100  * aclparse
101  *              Consumes and parses an ACL specification of the form:
102  *                              [group|user] [A-Za-z0-9]*[+-=][rwaR]*
103  *              from string 's', ignoring any leading white space or white space
104  *              between the optional id type keyword (group|user) and the actual
105  *              ACL specification.
106  *
107  *              This routine is called by the parser as well as aclitemin(), hence
108  *              the added generality.
109  *
110  * RETURNS:
111  *              the string position in 's' immediately following the ACL
112  *              specification.  Also:
113  *              - loads the structure pointed to by 'aip' with the appropriate
114  *                UID/GID, id type identifier and mode type values.
115  *              - loads 'modechg' with the mode change flag.
116  */
117 const char *
118 aclparse(const char *s, AclItem *aip, unsigned *modechg)
119 {
120         char            name[NAMEDATALEN];
121
122         Assert(s && aip && modechg);
123
124 #ifdef ACLDEBUG
125         elog(DEBUG, "aclparse: input = '%s'", s);
126 #endif
127         aip->ai_idtype = ACL_IDTYPE_UID;
128         s = getid(s, name);
129         if (*s != ACL_MODECHG_ADD_CHR &&
130                 *s != ACL_MODECHG_DEL_CHR &&
131                 *s != ACL_MODECHG_EQL_CHR)
132         {
133                 /* we just read a keyword, not a name */
134                 if (!strcmp(name, ACL_IDTYPE_GID_KEYWORD))
135                         aip->ai_idtype = ACL_IDTYPE_GID;
136                 else if (strcmp(name, ACL_IDTYPE_UID_KEYWORD))
137                         elog(ERROR, "aclparse: bad keyword, must be [group|user]");
138                 s = getid(s, name);             /* move s to the name beyond the keyword */
139                 if (name[0] == '\0')
140                         elog(ERROR, "aclparse: a name must follow the [group|user] keyword");
141         }
142         if (name[0] == '\0')
143                 aip->ai_idtype = ACL_IDTYPE_WORLD;
144
145         switch (*s)
146         {
147                 case ACL_MODECHG_ADD_CHR:
148                         *modechg = ACL_MODECHG_ADD;
149                         break;
150                 case ACL_MODECHG_DEL_CHR:
151                         *modechg = ACL_MODECHG_DEL;
152                         break;
153                 case ACL_MODECHG_EQL_CHR:
154                         *modechg = ACL_MODECHG_EQL;
155                         break;
156                 default:
157                         elog(ERROR, "aclparse: mode change flag must use \"%s\"",
158                                  ACL_MODECHG_STR);
159         }
160
161         aip->ai_mode = ACL_NO;
162         while (isalpha((unsigned char) *++s))
163         {
164                 switch (*s)
165                 {
166                         case ACL_MODE_INSERT_CHR:
167                                 aip->ai_mode |= ACL_INSERT;
168                                 break;
169                         case ACL_MODE_SELECT_CHR:
170                                 aip->ai_mode |= ACL_SELECT;
171                                 break;
172                         case ACL_MODE_UPDATE_CHR:
173                                 aip->ai_mode |= ACL_UPDATE;
174                                 break;
175                         case ACL_MODE_DELETE_CHR:
176                                 aip->ai_mode |= ACL_DELETE;
177                                 break;
178                         case ACL_MODE_RULE_CHR:
179                                 aip->ai_mode |= ACL_RULE;
180                                 break;
181                         case ACL_MODE_REFERENCES_CHR:
182                                 aip->ai_mode |= ACL_REFERENCES;
183                                 break;
184                         case ACL_MODE_TRIGGER_CHR:
185                                 aip->ai_mode |= ACL_TRIGGER;
186                                 break;
187                         default:
188                                 elog(ERROR, "aclparse: mode flags must use \"%s\"",
189                                          ACL_MODE_STR);
190                 }
191         }
192
193         switch (aip->ai_idtype)
194         {
195                 case ACL_IDTYPE_UID:
196                         aip->ai_id = get_usesysid(name);
197                         break;
198                 case ACL_IDTYPE_GID:
199                         aip->ai_id = get_grosysid(name);
200                         break;
201                 case ACL_IDTYPE_WORLD:
202                         aip->ai_id = ACL_ID_WORLD;
203                         break;
204         }
205
206 #ifdef ACLDEBUG
207         elog(DEBUG, "aclparse: correctly read [%x %d %x], modechg=%x",
208                  aip->ai_idtype, aip->ai_id, aip->ai_mode, *modechg);
209 #endif
210         return s;
211 }
212
213 /*
214  * makeacl
215  *              Allocates storage for a new Acl with 'n' entries.
216  *
217  * RETURNS:
218  *              the new Acl
219  */
220 Acl *
221 makeacl(int n)
222 {
223         Acl                *new_acl;
224         Size            size;
225
226         if (n < 0)
227                 elog(ERROR, "makeacl: invalid size: %d", n);
228         size = ACL_N_SIZE(n);
229         new_acl = (Acl *) palloc(size);
230         MemSet((char *) new_acl, 0, size);
231         new_acl->size = size;
232         new_acl->ndim = 1;
233         new_acl->flags = 0;
234         ARR_LBOUND(new_acl)[0] = 0;
235         ARR_DIMS(new_acl)[0] = n;
236         return new_acl;
237 }
238
239 /*
240  * aclitemin
241  *              Allocates storage for, and fills in, a new AclItem given a string
242  *              's' that contains an ACL specification.  See aclparse for details.
243  *
244  * RETURNS:
245  *              the new AclItem
246  */
247 Datum
248 aclitemin(PG_FUNCTION_ARGS)
249 {
250         const char *s = PG_GETARG_CSTRING(0);
251         AclItem    *aip;
252         unsigned        modechg;
253
254         aip = (AclItem *) palloc(sizeof(AclItem));
255         s = aclparse(s, aip, &modechg);
256         if (modechg != ACL_MODECHG_EQL)
257                 elog(ERROR, "aclitemin: cannot accept anything but = ACLs");
258         while (isspace((unsigned char) *s))
259                 ++s;
260         if (*s)
261                 elog(ERROR, "aclitemin: extra garbage at end of specification");
262         PG_RETURN_ACLITEM_P(aip);
263 }
264
265 /*
266  * aclitemout
267  *              Allocates storage for, and fills in, a new null-delimited string
268  *              containing a formatted ACL specification.  See aclparse for details.
269  *
270  * RETURNS:
271  *              the new string
272  */
273 Datum
274 aclitemout(PG_FUNCTION_ARGS)
275 {
276         AclItem    *aip = PG_GETARG_ACLITEM_P(0);
277         char       *p;
278         char       *out;
279         HeapTuple       htup;
280         unsigned        i;
281         char       *tmpname;
282
283         p = out = palloc(strlen("group =" ACL_MODE_STR " ") + 1 + NAMEDATALEN);
284         *p = '\0';
285
286         switch (aip->ai_idtype)
287         {
288                 case ACL_IDTYPE_UID:
289                         htup = SearchSysCache(SHADOWSYSID,
290                                                                   ObjectIdGetDatum(aip->ai_id),
291                                                                   0, 0, 0);
292                         if (HeapTupleIsValid(htup))
293                         {
294                                 strncat(p,
295                                         NameStr(((Form_pg_shadow) GETSTRUCT(htup))->usename),
296                                                 NAMEDATALEN);
297                                 ReleaseSysCache(htup);
298                         }
299                         else
300                         {
301                                 /* Generate numeric UID if we don't find an entry */
302                                 char       *tmp;
303
304                                 tmp = DatumGetCString(DirectFunctionCall1(int4out,
305                                                                          Int32GetDatum((int32) aip->ai_id)));
306                                 strcat(p, tmp);
307                                 pfree(tmp);
308                         }
309                         break;
310                 case ACL_IDTYPE_GID:
311                         strcat(p, "group ");
312                         tmpname = get_groname(aip->ai_id);
313                         if (tmpname != NULL)
314                                 strncat(p, tmpname, NAMEDATALEN);
315                         else
316                         {
317                                 /* Generate numeric GID if we don't find an entry */
318                                 char       *tmp;
319
320                                 tmp = DatumGetCString(DirectFunctionCall1(int4out,
321                                                                          Int32GetDatum((int32) aip->ai_id)));
322                                 strcat(p, tmp);
323                                 pfree(tmp);
324                         }
325                         break;
326                 case ACL_IDTYPE_WORLD:
327                         break;
328                 default:
329                         elog(ERROR, "aclitemout: bad ai_idtype: %d", aip->ai_idtype);
330                         break;
331         }
332         while (*p)
333                 ++p;
334         *p++ = '=';
335         for (i = 0; i < N_ACL_MODES; ++i)
336                 if ((aip->ai_mode >> i) & 01)
337                         *p++ = ACL_MODE_STR[i];
338         *p = '\0';
339
340         PG_RETURN_CSTRING(out);
341 }
342
343 /*
344  * aclitemeq
345  * aclitemgt
346  *              AclItem equality and greater-than comparison routines.
347  *              Two AclItems are considered equal iff they have the
348  *              same identifier (and identifier type); the mode is ignored.
349  *              Note that these routines are really only useful for sorting
350  *              AclItems into identifier order.
351  *
352  * RETURNS:
353  *              a boolean value indicating = or >
354  */
355 static bool
356 aclitemeq(const AclItem *a1, const AclItem *a2)
357 {
358         return a1->ai_idtype == a2->ai_idtype && a1->ai_id == a2->ai_id;
359 }
360
361 static bool
362 aclitemgt(const AclItem *a1, const AclItem *a2)
363 {
364         return ((a1->ai_idtype > a2->ai_idtype) ||
365                         (a1->ai_idtype == a2->ai_idtype && a1->ai_id > a2->ai_id));
366 }
367
368
369 /*
370  * acldefault()  --- create an ACL describing default access permissions
371  *
372  * Change this routine if you want to alter the default access policy for
373  * newly-created tables (or any table with a NULL acl entry in pg_class)
374  */
375 Acl *
376 acldefault(const char *relname, AclId ownerid)
377 {
378         Acl                *acl;
379         AclItem    *aip;
380
381 #define ACL_WORLD_DEFAULT               (ACL_NO)
382 #define ACL_OWNER_DEFAULT               (ACL_INSERT|ACL_SELECT|ACL_UPDATE|ACL_DELETE|ACL_RULE|ACL_REFERENCES|ACL_TRIGGER)
383
384         acl = makeacl(2);
385         aip = ACL_DAT(acl);
386         aip[0].ai_idtype = ACL_IDTYPE_WORLD;
387         aip[0].ai_id = ACL_ID_WORLD;
388         aip[0].ai_mode = IsSystemRelationName(relname) ? ACL_SELECT : ACL_WORLD_DEFAULT;
389         aip[1].ai_idtype = ACL_IDTYPE_UID;
390         aip[1].ai_id = ownerid;
391         aip[1].ai_mode = ACL_OWNER_DEFAULT;
392         return acl;
393 }
394
395
396 /*
397  * Add or replace an item in an ACL array.  The result is a modified copy;
398  * the input object is not changed.
399  *
400  * NB: caller is responsible for having detoasted the input ACL, if needed.
401  */
402 Acl *
403 aclinsert3(const Acl *old_acl, const AclItem *mod_aip, unsigned modechg)
404 {
405         Acl                *new_acl;
406         AclItem    *old_aip,
407                            *new_aip;
408         int                     dst,
409                                 num;
410
411         /* These checks for null input are probably dead code, but... */
412         if (!old_acl || ACL_NUM(old_acl) < 1)
413                 old_acl = makeacl(1);
414         if (!mod_aip)
415         {
416                 new_acl = makeacl(ACL_NUM(old_acl));
417                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
418                 return new_acl;
419         }
420
421         num = ACL_NUM(old_acl);
422         old_aip = ACL_DAT(old_acl);
423
424         /*
425          * Search the ACL for an existing entry for 'id'.  If one exists, just
426          * modify the entry in-place (well, in the same position, since we
427          * actually return a copy); otherwise, insert the new entry in
428          * sort-order.
429          */
430         /* find the first element not less than the element to be inserted */
431         for (dst = 0; dst < num && aclitemgt(mod_aip, old_aip + dst); ++dst)
432                 ;
433
434         if (dst < num && aclitemeq(mod_aip, old_aip + dst))
435         {
436                 /* found a match, so modify existing item */
437                 new_acl = makeacl(num);
438                 new_aip = ACL_DAT(new_acl);
439                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
440         }
441         else
442         {
443                 /* need to insert a new item */
444                 new_acl = makeacl(num + 1);
445                 new_aip = ACL_DAT(new_acl);
446                 if (dst == 0)
447                 {                                               /* start */
448                         elog(ERROR, "aclinsert3: insertion before world ACL??");
449                 }
450                 else if (dst >= num)
451                 {                                               /* end */
452                         memcpy((char *) new_aip,
453                                    (char *) old_aip,
454                                    num * sizeof(AclItem));
455                 }
456                 else
457                 {                                               /* middle */
458                         memcpy((char *) new_aip,
459                                    (char *) old_aip,
460                                    dst * sizeof(AclItem));
461                         memcpy((char *) (new_aip + dst + 1),
462                                    (char *) (old_aip + dst),
463                                    (num - dst) * sizeof(AclItem));
464                 }
465                 /* initialize the new entry with no permissions */
466                 new_aip[dst].ai_id = mod_aip->ai_id;
467                 new_aip[dst].ai_idtype = mod_aip->ai_idtype;
468                 new_aip[dst].ai_mode = 0;
469                 num++;                                  /* set num to the size of new_acl */
470         }
471
472         /* apply the permissions mod */
473         switch (modechg)
474         {
475                 case ACL_MODECHG_ADD:
476                         new_aip[dst].ai_mode |= mod_aip->ai_mode;
477                         break;
478                 case ACL_MODECHG_DEL:
479                         new_aip[dst].ai_mode &= ~mod_aip->ai_mode;
480                         break;
481                 case ACL_MODECHG_EQL:
482                         new_aip[dst].ai_mode = mod_aip->ai_mode;
483                         break;
484         }
485
486         /*
487          * if the adjusted entry has no permissions, delete it from the list.
488          * For example, this helps in removing entries for users who no longer
489          * exist.  EXCEPTION: never remove the world entry.
490          */
491         if (new_aip[dst].ai_mode == 0 && dst > 0)
492         {
493                 memmove((char *) (new_aip + dst),
494                                 (char *) (new_aip + dst + 1),
495                                 (num - dst - 1) * sizeof(AclItem));
496                 ARR_DIMS(new_acl)[0] = num - 1;
497                 ARR_SIZE(new_acl) -= sizeof(AclItem);
498         }
499
500         return new_acl;
501 }
502
503 /*
504  * aclinsert (exported function)
505  */
506 Datum
507 aclinsert(PG_FUNCTION_ARGS)
508 {
509         Acl                *old_acl = PG_GETARG_ACL_P(0);
510         AclItem    *mod_aip = PG_GETARG_ACLITEM_P(1);
511
512         PG_RETURN_ACL_P(aclinsert3(old_acl, mod_aip, ACL_MODECHG_EQL));
513 }
514
515 Datum
516 aclremove(PG_FUNCTION_ARGS)
517 {
518         Acl                *old_acl = PG_GETARG_ACL_P(0);
519         AclItem    *mod_aip = PG_GETARG_ACLITEM_P(1);
520         Acl                *new_acl;
521         AclItem    *old_aip,
522                            *new_aip;
523         int                     dst,
524                                 old_num,
525                                 new_num;
526
527         /* These checks for null input should be dead code, but... */
528         if (!old_acl || ACL_NUM(old_acl) < 1)
529                 old_acl = makeacl(1);
530         if (!mod_aip)
531         {
532                 new_acl = makeacl(ACL_NUM(old_acl));
533                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
534                 PG_RETURN_ACL_P(new_acl);
535         }
536
537         old_num = ACL_NUM(old_acl);
538         old_aip = ACL_DAT(old_acl);
539
540         /* Search for the matching entry */
541         for (dst = 0; dst < old_num && !aclitemeq(mod_aip, old_aip + dst); ++dst)
542                 ;
543
544         if (dst >= old_num)
545         {
546                 /* Not found, so return copy of source ACL */
547                 new_acl = makeacl(old_num);
548                 memcpy((char *) new_acl, (char *) old_acl, ACL_SIZE(old_acl));
549         }
550         else
551         {
552                 new_num = old_num - 1;
553                 new_acl = makeacl(new_num);
554                 new_aip = ACL_DAT(new_acl);
555                 if (dst == 0)
556                 {                                               /* start */
557                         elog(ERROR, "aclremove: removal of the world ACL??");
558                 }
559                 else if (dst == old_num - 1)
560                 {                                               /* end */
561                         memcpy((char *) new_aip,
562                                    (char *) old_aip,
563                                    new_num * sizeof(AclItem));
564                 }
565                 else
566                 {                                               /* middle */
567                         memcpy((char *) new_aip,
568                                    (char *) old_aip,
569                                    dst * sizeof(AclItem));
570                         memcpy((char *) (new_aip + dst),
571                                    (char *) (old_aip + dst + 1),
572                                    (new_num - dst) * sizeof(AclItem));
573                 }
574         }
575
576         PG_RETURN_ACL_P(new_acl);
577 }
578
579 Datum
580 aclcontains(PG_FUNCTION_ARGS)
581 {
582         Acl                *acl = PG_GETARG_ACL_P(0);
583         AclItem    *aip = PG_GETARG_ACLITEM_P(1);
584         AclItem    *aidat;
585         int                     i,
586                                 num;
587
588         num = ACL_NUM(acl);
589         aidat = ACL_DAT(acl);
590         for (i = 0; i < num; ++i)
591         {
592                 /* Note that aclitemeq only considers id, not mode */
593                 if (aclitemeq(aip, aidat + i) &&
594                         aip->ai_mode == aidat[i].ai_mode)
595                         PG_RETURN_BOOL(true);
596         }
597         PG_RETURN_BOOL(false);
598 }
599
600
601 /*
602  * Parser support routines for ACL-related statements.
603  *
604  * XXX CAUTION: these are called from gram.y, which is not allowed to
605  * do any table accesses.  Therefore, it is not kosher to do things
606  * like trying to translate usernames to user IDs here.  Keep it all
607  * in string form until statement execution time.
608  */
609
610 /*
611  * aclmakepriv
612  *        make a acl privilege string out of an existing privilege string
613  * and a new privilege
614  *
615  * does not add duplicate privileges
616  */
617 char *
618 aclmakepriv(const char *old_privlist, char new_priv)
619 {
620         char       *priv;
621         int                     i;
622         int                     l;
623
624         Assert(strlen(old_privlist) <= strlen(ACL_MODE_STR));
625         priv = palloc(strlen(ACL_MODE_STR)+1);
626
627         if (old_privlist == NULL || old_privlist[0] == '\0')
628         {
629                 priv[0] = new_priv;
630                 priv[1] = '\0';
631                 return priv;
632         }
633
634         strcpy(priv, old_privlist);
635
636         l = strlen(old_privlist);
637
638         if (l == strlen(ACL_MODE_STR))
639         {                                                       /* can't add any more privileges */
640                 return priv;
641         }
642
643         /* check to see if the new privilege is already in the old string */
644         for (i = 0; i < l; i++)
645         {
646                 if (priv[i] == new_priv)
647                         break;
648         }
649         if (i == l)
650         {                                                       /* we really have a new privilege */
651                 priv[l] = new_priv;
652                 priv[l + 1] = '\0';
653         }
654
655         return priv;
656 }
657
658 /*
659  * aclmakeuser
660  *        user_type must be "A"  - all users
661  *                                              "G"  - group
662  *                                              "U"  - user
663  *
664  * Just concatenates the two strings together with a space in between.
665  * Per above comments, we can't try to resolve a user or group name here.
666  */
667 char *
668 aclmakeuser(const char *user_type, const char *user)
669 {
670         char       *user_list;
671
672         user_list = palloc(strlen(user_type) + strlen(user) + 2);
673         sprintf(user_list, "%s %s", user_type, user);
674         return user_list;
675 }
676
677
678 /*
679  * makeAclString:  We take in the privileges and grantee as well as a
680  * single character '+' or '-' to indicate grant or revoke.
681  *
682  * We convert the information to the same external form recognized by
683  * aclitemin (see aclparse) and return that string.  Conversion to
684  * internal form happens when the statement is executed.
685  */
686 char *
687 makeAclString(const char *privileges, const char *grantee, char grant_or_revoke)
688 {
689         StringInfoData str;
690         char *ret;
691
692         initStringInfo(&str);
693
694         /* the grantee string is "G <group_name>", "U <user_name>", or "ALL" */
695         if (grantee[0] == 'G')          /* group permissions */
696         {
697                 appendStringInfo(&str, "%s \"%s\"%c%s",
698                                                  ACL_IDTYPE_GID_KEYWORD,
699                                                  grantee + 2, grant_or_revoke, privileges);
700         }
701         else if (grantee[0] == 'U') /* user permission */
702         {
703                 appendStringInfo(&str, "%s \"%s\"%c%s",
704                                                  ACL_IDTYPE_UID_KEYWORD,
705                                                  grantee + 2, grant_or_revoke, privileges);
706         }
707         else
708         {
709                 /* all permission */
710                 appendStringInfo(&str, "%c%s",
711                                                  grant_or_revoke, privileges);
712         }
713         ret = pstrdup(str.data);
714         pfree(str.data);
715         return ret;
716 }
717
718
719 /*
720  * has_table_privilege_name_name
721  *              Check user privileges on a relation given
722  *              name usename, name relname, and text priv name.
723  *
724  * RETURNS
725  *              a boolean value
726  *              't' indicating user has the privilege
727  *              'f' indicating user does not have the privilege
728  */
729 Datum
730 has_table_privilege_name_name(PG_FUNCTION_ARGS)
731 {
732         Name            username = PG_GETARG_NAME(0);
733         Name            relname = PG_GETARG_NAME(1);
734         text            *priv_type_text = PG_GETARG_TEXT_P(2);
735         bool            result;
736
737         result = has_table_privilege_cname_cname(NameStr(*username),
738                                                                                          NameStr(*relname),
739                                                                                          priv_type_text);
740
741         PG_RETURN_BOOL(result);
742 }
743
744
745 /*
746  * has_table_privilege_name
747  *              Check user privileges on a relation given
748  *              name relname and text priv name.
749  *              current_user is assumed
750  *
751  * RETURNS
752  *              a boolean value
753  *              't' indicating user has the privilege
754  *              'f' indicating user does not have the privilege
755  */
756 Datum
757 has_table_privilege_name(PG_FUNCTION_ARGS)
758 {
759         Name            relname = PG_GETARG_NAME(0);
760         text            *priv_type_text = PG_GETARG_TEXT_P(1);
761         int32           usesysid;
762         bool            result;
763
764         usesysid = GetUserId();
765
766         result = has_table_privilege_id_cname(usesysid,
767                                                                                   NameStr(*relname),
768                                                                                   priv_type_text);
769
770         PG_RETURN_BOOL(result);
771 }
772
773
774 /*
775  * has_table_privilege_name_id
776  *              Check user privileges on a relation given
777  *              name usename, rel oid, and text priv name.
778  *
779  * RETURNS
780  *              a boolean value
781  *              't' indicating user has the privilege
782  *              'f' indicating user does not have the privilege
783  */
784 Datum
785 has_table_privilege_name_id(PG_FUNCTION_ARGS)
786 {
787         Name            username = PG_GETARG_NAME(0);
788         Oid                     reloid = PG_GETARG_OID(1);
789         text            *priv_type_text = PG_GETARG_TEXT_P(2);
790         bool            result;
791
792         result = has_table_privilege_cname_id(NameStr(*username),
793                                                                                   reloid,
794                                                                                   priv_type_text);
795
796         PG_RETURN_BOOL(result);
797 }
798
799
800 /*
801  * has_table_privilege_id
802  *              Check user privileges on a relation given
803  *              rel oid, and text priv name.
804  *              current_user is assumed
805  *
806  * RETURNS
807  *              a boolean value
808  *              't' indicating user has the privilege
809  *              'f' indicating user does not have the privilege
810  */
811 Datum
812 has_table_privilege_id(PG_FUNCTION_ARGS)
813 {
814         Oid                     reloid = PG_GETARG_OID(0);
815         text            *priv_type_text = PG_GETARG_TEXT_P(1);
816         char       *relname;
817         int32           usesysid;
818         AclMode         mode;
819         int32           result;
820
821         usesysid = GetUserId();
822
823         /*
824          * Lookup relname based on rel oid
825          */
826         relname = get_rel_name(reloid);
827         if (relname == NULL)
828                 elog(ERROR, "has_table_privilege: invalid relation oid %u",
829                          reloid);
830
831         /* 
832          * Convert priv_type_text to an AclMode
833          */
834         mode = convert_priv_string(priv_type_text);
835
836         /* 
837          * Finally, check for the privilege
838          */
839         result = pg_aclcheck(relname, usesysid, mode);
840
841         if (result == ACLCHECK_OK)
842                 PG_RETURN_BOOL(true);
843         else
844                 PG_RETURN_BOOL(false);
845 }
846
847
848 /*
849  * has_table_privilege_id_name
850  *              Check user privileges on a relation given
851  *              usesysid, name relname, and priv name.
852  *
853  * RETURNS
854  *              a boolean value
855  *              't' indicating user has the privilege
856  *              'f' indicating user does not have the privilege
857  */
858 Datum
859 has_table_privilege_id_name(PG_FUNCTION_ARGS)
860 {
861         int32           usesysid = PG_GETARG_INT32(0);
862         Name            relname = PG_GETARG_NAME(1);
863         text            *priv_type_text = PG_GETARG_TEXT_P(2);
864         bool            result;
865
866         result = has_table_privilege_id_cname(usesysid,
867                                                                                   NameStr(*relname),
868                                                                                   priv_type_text);
869
870         PG_RETURN_BOOL(result);
871 }
872
873
874 /*
875  * has_table_privilege_id_id
876  *              Check user privileges on a relation given
877  *              usesysid, rel oid, and priv name.
878  *
879  * RETURNS
880  *              a boolean value
881  *              't' indicating user has the privilege
882  *              'f' indicating user does not have the privilege
883  */
884 Datum
885 has_table_privilege_id_id(PG_FUNCTION_ARGS)
886 {
887         int32           usesysid = PG_GETARG_INT32(0);
888         Oid                     reloid = PG_GETARG_OID(1);
889         text            *priv_type_text = PG_GETARG_TEXT_P(2);
890         char            *relname;
891         AclMode         mode;
892         int32           result;
893
894         /*
895          * Lookup relname based on rel oid
896          */
897         relname = get_rel_name(reloid);
898         if (relname == NULL)
899                 elog(ERROR, "has_table_privilege: invalid relation oid %u",
900                          reloid);
901
902         /* 
903          * Convert priv_type_text to an AclMode
904          */
905         mode = convert_priv_string(priv_type_text);
906
907         /* 
908          * Finally, check for the privilege
909          */
910         result = pg_aclcheck(relname, usesysid, mode);
911
912         if (result == ACLCHECK_OK)
913                 PG_RETURN_BOOL(true);
914         else
915                 PG_RETURN_BOOL(false);
916 }
917
918 /*
919  *              Internal functions.
920  */
921
922 /*
923  * convert_priv_string
924  *              Internal function.
925  *              Return mode from priv_type string
926  *
927  * RETURNS
928  *              AclMode
929  */
930
931 static AclMode
932 convert_priv_string(text *priv_type_text)
933 {
934         char    *priv_type = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(priv_type_text)));
935
936         /*
937          * Return mode from priv_type string
938          */
939         if (strcasecmp(priv_type, "SELECT") == 0)
940                 return ACL_SELECT;
941
942         if (strcasecmp(priv_type, "INSERT") == 0)
943                 return ACL_INSERT;
944
945         if (strcasecmp(priv_type, "UPDATE") == 0)
946                 return ACL_UPDATE;
947
948         if (strcasecmp(priv_type, "DELETE") == 0)
949                 return ACL_DELETE;
950
951         if (strcasecmp(priv_type, "RULE") == 0)
952                 return ACL_RULE;
953
954         if (strcasecmp(priv_type, "REFERENCES") == 0)
955                 return ACL_REFERENCES;
956
957         if (strcasecmp(priv_type, "TRIGGER") == 0)
958                 return ACL_TRIGGER;
959
960         elog(ERROR, "has_table_privilege: invalid privilege type %s", priv_type);
961         /*
962          * We should never get here, but stop the compiler from complaining
963          */
964         return ACL_NO;
965 }
966
967 /*
968  * has_table_privilege_cname_cname
969  *              Check user privileges on a relation given
970  *              char *usename, char *relname, and text priv name.
971  *
972  * RETURNS
973  *              a boolean value
974  *              't' indicating user has the privilege
975  *              'f' indicating user does not have the privilege
976  */
977 static bool
978 has_table_privilege_cname_cname(char *username, char *relname,
979                                                                 text *priv_type_text)
980 {
981         int32           usesysid;
982
983         /*
984          * Lookup userid based on username
985          */
986         usesysid = get_usesysid(username);
987
988         /*
989          * Make use of has_table_privilege_id_cname.
990          * It accepts the arguments we now have.
991          */
992         return has_table_privilege_id_cname(usesysid, relname, priv_type_text);
993 }
994
995
996 /*
997  * has_table_privilege_cname_id
998  *              Check user privileges on a relation given
999  *              char *usename, rel oid, and text priv name.
1000  *
1001  * RETURNS
1002  *              a boolean value
1003  *              't' indicating user has the privilege
1004  *              'f' indicating user does not have the privilege
1005  */
1006 static bool
1007 has_table_privilege_cname_id(char *username, Oid reloid,
1008                                                          text *priv_type_text)
1009 {
1010         int32           usesysid;
1011         char            *relname;
1012
1013         /*
1014          * Lookup userid based on username
1015          */
1016         usesysid = get_usesysid(username);
1017
1018         /*
1019          * Lookup relname based on rel oid
1020          */
1021         relname = get_rel_name(reloid);
1022         if (relname == NULL)
1023                 elog(ERROR, "has_table_privilege: invalid relation oid %u",
1024                          reloid);
1025
1026         /*
1027          * Make use of has_table_privilege_id_cname.
1028          * It accepts the arguments we now have.
1029          */
1030         return has_table_privilege_id_cname(usesysid, relname, priv_type_text);
1031 }
1032
1033
1034 /*
1035  * has_table_privilege_id_cname
1036  *              Check user privileges on a relation given
1037  *              usesysid, char *relname, and text priv name.
1038  *
1039  * RETURNS
1040  *              a boolean value
1041  *              't' indicating user has the privilege
1042  *              'f' indicating user does not have the privilege
1043  */
1044 static bool
1045 has_table_privilege_id_cname(int32 usesysid, char *relname,
1046                                                          text *priv_type_text)
1047 {
1048         HeapTuple       tuple;
1049         AclMode         mode;
1050         int32           result;
1051
1052         /*
1053          * Check relname is valid.
1054          * This is needed to deal with the case when usename is a superuser
1055          * in which case pg_aclcheck simply returns ACLCHECK_OK
1056          * without validating relname
1057          */
1058         tuple = SearchSysCache(RELNAME,
1059                                                    PointerGetDatum(relname),
1060                                                    0, 0, 0);
1061         if (!HeapTupleIsValid(tuple))
1062                 elog(ERROR, "has_table_privilege: relation \"%s\" does not exist",
1063                          relname);
1064         ReleaseSysCache(tuple);
1065
1066         /* 
1067          * Convert priv_type_text to an AclMode
1068          */
1069         mode = convert_priv_string(priv_type_text);
1070
1071         /* 
1072          * Finally, check for the privilege
1073          */
1074         result = pg_aclcheck(relname, usesysid, mode);
1075
1076         if (result == ACLCHECK_OK)
1077                 return true;
1078         else
1079                 return false;
1080 }