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