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