• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tarantool / tarantool / 5653

pending completion
5653

push

travis-ci

rtsisyk
vinyl: vinyl/large.test.lua is too large

2Mb tuple is overkill for 1k page.

Fixes #1868

21209 of 25630 relevant lines covered (82.75%)

881605.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.98
/src/box/user.cc
1
/*
2
 * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
3
 *
4
 * Redistribution and use in source and binary forms, with or
5
 * without modification, are permitted provided that the following
6
 * conditions are met:
7
 *
8
 * 1. Redistributions of source code must retain the above
9
 *    copyright notice, this list of conditions and the
10
 *    following disclaimer.
11
 *
12
 * 2. Redistributions in binary form must reproduce the above
13
 *    copyright notice, this list of conditions and the following
14
 *    disclaimer in the documentation and/or other materials
15
 *    provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21
 * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 */
31
#include "user.h"
32
#include "user_def.h"
33
#include "assoc.h"
34
#include "schema.h"
35
#include "space.h"
36
#include "memtx_index.h"
37
#include "func.h"
38
#include "index.h"
39
#include "bit/bit.h"
40
#include "session.h"
41

42
struct universe universe;
43
static struct user users[BOX_USER_MAX];
44
struct user *guest_user = users;
45
struct user *admin_user = users + 1;
46

47
static struct user_map user_map_nil;
48

49
struct mh_i32ptr_t *user_registry;
50

51
/* {{{ user_map */
52

53
static inline int
54
user_map_calc_idx(uint8_t auth_token, uint8_t *bit_no)
55
{
56
        *bit_no = auth_token & (UMAP_INT_BITS - 1);
14,830✔
57
        return auth_token / UMAP_INT_BITS;
8,710✔
58
}
59

60

61
/** Set a bit in the user map - add a user. */
62
static inline void
63
user_map_set(struct user_map *map, uint8_t auth_token)
64
{
65
        uint8_t bit_no;
66
        int idx = user_map_calc_idx(auth_token, &bit_no);
13,966✔
67
        map->m[idx] |= ((umap_int_t) 1) << bit_no;
13,966✔
68
}
69

70
/** Clear a bit in the user map - remove a user. */
71
static inline void
72
user_map_clear(struct user_map *map, uint8_t auth_token)
73
{
74
        uint8_t bit_no;
75
        int idx = user_map_calc_idx(auth_token, &bit_no);
244✔
76
        map->m[idx] &= ~(((umap_int_t) 1) << bit_no);
244✔
77
}
78

79
/* Check if a bit is set in the user map. */
80
static inline bool
81
user_map_is_set(struct user_map *map, uint8_t auth_token)
82
{
83
        uint8_t bit_no;
84
        int idx = user_map_calc_idx(auth_token, &bit_no);
506✔
85
        return map->m[idx] & (((umap_int_t) 1) << bit_no);
506✔
86
}
87

88
/**
89
 * Merge two sets of users: add all users from right argument
90
 * to the left one.
91
 */
92
static void
93
user_map_union(struct user_map *lhs, struct user_map *rhs)
94
{
95
        for (int i = 0; i < USER_MAP_SIZE; i++)
34,612✔
96
                lhs->m[i] |= rhs->m[i];
29,027✔
97
}
98

99
/**
100
 * Remove all users present in rhs from lhs
101
 */
102
static void
103
user_map_minus(struct user_map *lhs, struct user_map *rhs)
104
{
105
        for (int i = 0; i < USER_MAP_SIZE; i++)
36,013✔
106
                lhs->m[i] &= ~rhs->m[i];
22,408✔
107
}
108

109
/** Iterate over users in the set of users. */
110
struct user_map_iterator
111
{
112
        struct bit_iterator it;
113
};
114

115
static void
116
user_map_iterator_init(struct user_map_iterator *it, struct user_map *map)
117
{
118
        bit_iterator_init(&it->it, map->m,
119
                          USER_MAP_SIZE * sizeof(umap_int_t), true);
20,442✔
120
}
121

122
static struct user *
123
user_map_iterator_next(struct user_map_iterator *it)
124
{
125
        size_t auth_token = bit_iterator_next(&it->it);
43,267✔
126
        if (auth_token != SIZE_MAX)
43,267✔
127
                return users + auth_token;
22,825✔
128
        return NULL;
129
}
130

131
/* }}} */
132

133
/* {{{ privset_t - set of effective privileges of a user */
134

135
extern "C" {
136

137
static int
138
priv_def_compare(const struct priv_def *lhs, const struct priv_def *rhs)
139
{
140
        if (lhs->object_type != rhs->object_type)
132,502✔
141
                return lhs->object_type > rhs->object_type ? 1 : -1;
37,094✔
142
        if (lhs->object_id != rhs->object_id)
95,408✔
143
                return lhs->object_id > rhs->object_id ? 1 : -1;
56,627✔
144
        return 0;
145
}
146

147
} /* extern "C" */
148

149
rb_gen(, privset_, privset_t, struct priv_def, link, priv_def_compare);
250,126✔
150

151
/* }}}  */
152

153
/** {{{ user */
154

155
static void
156
user_create(struct user *user, uint8_t auth_token)
157
{
158
        assert(user->auth_token == 0);
159
        user->auth_token = auth_token;
1,340✔
160
        privset_new(&user->privs);
1,340✔
161
        region_create(&user->pool, &cord()->slabc);
1,340✔
162
}
163

164
static void
165
user_destroy(struct user *user)
114✔
166
{
167
        /*
168
         * Sic: we don't have to remove a deleted
169
         * user from users set of roles, since
170
         * to drop a user, one has to revoke
171
         * all privileges from them first.
172
         */
173
        region_destroy(&user->pool);
114✔
174
        memset(user, 0, sizeof(*user));
175
}
114✔
176

177
/**
178
 * Add a privilege definition to the list
179
 * of effective privileges of a user.
180
 */
181
void
182
user_grant_priv(struct user *user, struct priv_def *def)
18,054✔
183
{
184
        struct priv_def *old = privset_search(&user->privs, def);
18,054✔
185
        if (old == NULL) {
18,054✔
186
                old = (struct priv_def *)
187
                        region_alloc_xc(&user->pool, sizeof(struct priv_def));
18,027✔
188
                *old = *def;
18,027✔
189
                privset_insert(&user->privs, old);
18,027✔
190
        } else {
191
                old->access |= def->access;
27✔
192
        }
193
}
18,054✔
194

195
/**
196
 * Find the corresponding access structure
197
 * given object type and object id.
198
 */
199
struct access *
200
access_find(struct priv_def *priv)
34,342✔
201
{
202
        struct access *access = NULL;
34,342✔
203
        switch (priv->object_type) {
34,342✔
204
        case SC_UNIVERSE:
205
        {
206
                access = universe.access;
1,990✔
207
                break;
1,990✔
208
        }
209
        case SC_SPACE:
210
        {
211
                struct space *space = space_by_id(priv->object_id);
24,257✔
212
                if (space)
24,257✔
213
                        access = space->access;
24,257✔
214
                break;
215
        }
216
        case SC_FUNCTION:
217
        {
218
                struct func *func = func_by_id(priv->object_id);
8,095✔
219
                if (func)
8,095✔
220
                        access = func->access;
8,095✔
221
                break;
222
        }
223
        default:
224
                break;
225
        }
226
        return access;
34,342✔
227
}
228

229

230
/**
231
 * Reset effective access of the user in the
232
 * corresponding objects.
233
 */
234
static void
235
user_set_effective_access(struct user *user)
11,170✔
236
{
237
        struct credentials *cr = current_user();
11,170✔
238
        struct priv_def *priv;
239
        for (priv = privset_first(&user->privs);
53,632✔
240
             priv;
241
             priv = privset_next(&user->privs, priv)) {
31,292✔
242
                struct access *object = access_find(priv);
31,292✔
243
                 /* Protect against a concurrent drop. */
244
                if (object == NULL)
31,292✔
245
                        continue;
246
                struct access *access = &object[user->auth_token];
31,292✔
247
                access->effective = access->granted | priv->access;
31,292✔
248
                /** Update global access in the current session. */
249
                if (priv->object_type == SC_UNIVERSE &&
32,456✔
250
                    user->def.uid == cr->uid) {
1,164✔
251
                        cr->universal_access = access->effective;
287✔
252
                }
253
        }
254
}
11,170✔
255

256
/**
257
 * Reload user privileges and re-grant them.
258
 */
259
static void
260
user_reload_privs(struct user *user)
11,051✔
261
{
262
        if (user->is_dirty == false)
11,051✔
263
                return;
11,051✔
264
        struct priv_def *priv;
265
        /**
266
         * Reset effective access of the user in the
267
         * corresponding objects to have
268
         * only the stuff that it's granted directly.
269
         */
270
        for (priv = privset_first(&user->privs);
24,435✔
271
             priv;
272
             priv = privset_next(&user->privs, priv)) {
13,265✔
273
                priv->access = 0;
13,265✔
274
        }
275
        user_set_effective_access(user);
5,585✔
276
        region_free(&user->pool);
5,585✔
277
        privset_new(&user->privs);
5,585✔
278
        /* Load granted privs from _priv space. */
279
        {
280
                struct space *space = space_cache_find(BOX_PRIV_ID);
5,585✔
281
                char key[6];
282
                /** Primary key - by user id */
283
                MemtxIndex *index = index_find_system(space, 0);
5,585✔
284
                mp_encode_uint(key, user->def.uid);
5,585✔
285

286
                struct iterator *it = index->position();
5,585✔
287
                index->initIterator(it, ITER_EQ, key, 1);
5,585✔
288

289
                struct tuple *tuple;
290
                while ((tuple = it->next(it))) {
16,565✔
291
                        struct priv_def priv;
292
                        priv_def_create_from_tuple(&priv, tuple);
10,980✔
293
                        /**
294
                         * Skip role grants, we're only
295
                         * interested in real objects.
296
                         */
297
                        if (priv.object_type != SC_ROLE)
10,980✔
298
                                user_grant_priv(user, &priv);
7,864✔
299
                }
300
        }
301
        {
302
                /* Take into account privs granted through roles. */
303
                struct user_map_iterator it;
304
                user_map_iterator_init(&it, &user->roles);
5,585✔
305
                struct user *role;
306
                while ((role = user_map_iterator_next(&it))) {
8,701✔
307
                        struct priv_def *def = privset_first(&role->privs);
6,232✔
308
                        while (def) {
16,422✔
309
                                user_grant_priv(user, def);
10,190✔
310
                                def = privset_next(&role->privs, def);
10,190✔
311
                        }
312
                }
313
        }
314
        user_set_effective_access(user);
5,585✔
315
        user->is_dirty = false;
5,585✔
316
}
317

318
/** }}} */
319

320
/* {{{ authentication tokens */
321

322
/** A map to quickly look up free slots in users[] array. */
323
static umap_int_t tokens[USER_MAP_SIZE];
324
/**
325
 * Index of the minimal element of the tokens array which
326
 * has an unused token.
327
 */
328
static int min_token_idx = 0;
329

330
/**
331
 * Find and return a spare authentication token.
332
 * Raise an exception when the maximal number of users
333
 * is reached (and we're out of tokens).
334
 */
335
uint8_t
336
auth_token_get()
1,341✔
337
{
338
        uint8_t bit_no = 0;
1,341✔
339
        while (min_token_idx < USER_MAP_SIZE) {
2,683✔
340
                bit_no = __builtin_ffs(tokens[min_token_idx]);
1,341✔
341
                if (bit_no)
1,341✔
342
                        break;
343
                min_token_idx++;
1✔
344
        }
345
        if (bit_no == 0 || bit_no > BOX_USER_MAX) {
1,341✔
346
                /* A cap on the number of users was reached.
347
                 * Check for BOX_USER_MAX to cover case when
348
                 * USER_MAP_BITS > BOX_USER_MAX.
349
                 */
350
                tnt_raise(LoggedError, ER_USER_MAX, BOX_USER_MAX);
2✔
351
        }
352
        /*
353
         * find-first-set returns bit index starting from 1,
354
         * or 0 if no bit is set. Rebase the index to offset 0.
355
         */
356
        bit_no--;
1,340✔
357
        tokens[min_token_idx] ^= ((umap_int_t) 1) << bit_no;
1,340✔
358
        int auth_token = min_token_idx * UMAP_INT_BITS + bit_no;
1,340✔
359
        assert(auth_token < UINT8_MAX);
360
        return auth_token;
1,340✔
361
}
362

363
/**
364
 * Return an authentication token to the set of unused
365
 * tokens.
366
 */
367
void
368
auth_token_put(uint8_t auth_token)
×
369
{
370
        uint8_t bit_no;
371
        int idx = user_map_calc_idx(auth_token, &bit_no);
114✔
372
        tokens[idx] |= ((umap_int_t) 1) << bit_no;
114✔
373
        if (idx < min_token_idx)
114✔
374
                min_token_idx = idx;
1✔
375
}
×
376

377
/* }}} */
378

379
/* {{{ user cache */
380

381
struct user *
382
user_cache_replace(struct user_def *def)
1,928✔
383
{
384
        struct user *user = user_by_id(def->uid);
1,928✔
385
        if (user == NULL) {
1,928✔
386
                uint8_t auth_token = auth_token_get();
1,341✔
387
                user = users + auth_token;
1,340✔
388
                user_create(user, auth_token);
1,340✔
389
                struct mh_i32ptr_node_t node = { def->uid, user };
1,340✔
390
                mh_i32ptr_put(user_registry, &node, NULL, NULL);
1,340✔
391
        }
392
        *(struct user_def *) user = *def;
1,927✔
393
        return user;
1,927✔
394
}
395

396
void
397
user_cache_delete(uint32_t uid)
114✔
398
{
399
        struct user *user = user_by_id(uid);
114✔
400
        if (user) {
114✔
401
                assert(user->auth_token > ADMIN);
402
                auth_token_put(user->auth_token);
114✔
403
                assert(user_map_is_empty(&user->roles));
404
                assert(user_map_is_empty(&user->users));
405
                /*
406
                 * Sic: we don't have to remove a deleted
407
                 * user from users hash of roles, since
408
                 * to drop a user, one has to revoke
409
                 * all privileges from them first.
410
                 */
411
                user_destroy(user);
114✔
412
                mh_i32ptr_del(user_registry, uid, NULL);
114✔
413
        }
414
}
114✔
415

416
/** Find user by id. */
417
struct user *
418
user_by_id(uint32_t uid)
15,435✔
419
{
420
        mh_int_t k = mh_i32ptr_find(user_registry, uid, NULL);
28,752✔
421
        if (k == mh_end(user_registry))
15,435✔
422
                return NULL;
423
        return (struct user *) mh_i32ptr_node(user_registry, k)->val;
26,634✔
424
}
425

426
struct user *
427
user_find(uint32_t uid)
3,466✔
428
{
429
        struct user *user = user_by_id(uid);
3,466✔
430
        if (user == NULL)
3,466✔
431
                diag_set(ClientError, ER_NO_SUCH_USER, int2str(uid));
1✔
432
        return user;
3,466✔
433
}
434

435
/** Find user by name. */
436
struct user *
437
user_find_by_name(const char *name, uint32_t len)
245✔
438
{
439
        uint32_t uid = schema_find_id(BOX_USER_ID, 2, name, len);
245✔
440
        struct user *user = user_by_id(uid);
245✔
441
        if (user == NULL || user->def.type != SC_USER) {
245✔
442
                char name_buf[BOX_NAME_MAX + 1];
443
                /* \0 - to correctly print user name the error message. */
444
                snprintf(name_buf, sizeof(name_buf), "%.*s", len, name);
445
                diag_set(ClientError, ER_NO_SUCH_USER, name_buf);
4✔
446
                return NULL;
447
        }
448
        return user;
449
}
450

451
void
452
user_cache_init()
289✔
453
{
454
        /** Mark all tokens as unused. */
455
        memset(tokens, 0xFF, sizeof(tokens));
456
        user_registry = mh_i32ptr_new();
289✔
457
        /*
458
         * Solve a chicken-egg problem:
459
         * we need a functional user cache entry for superuser to
460
         * perform recovery, but the superuser credentials are
461
         * stored in the snapshot. So, pre-create cache entries
462
         * for 'guest' and 'admin' users here, they will be
463
         * updated with snapshot contents during recovery.
464
         */
465
        struct user_def def;
466
        memset(&def, 0, sizeof(def));
467
        snprintf(def.name, sizeof(def.name), "guest");
468
        def.owner = ADMIN;
289✔
469
        def.type = SC_USER;
289✔
470
        struct user *user = user_cache_replace(&def);
289✔
471
        /* 0 is the auth token and user id by default. */
472
        assert(user->def.uid == GUEST && user->auth_token == GUEST);
473
        (void)user;
474

475
        memset(&def, 0, sizeof(def));
476
        snprintf(def.name, sizeof(def.name), "admin");
477
        def.uid = def.owner = ADMIN;
289✔
478
        def.type = SC_USER;
289✔
479
        user = user_cache_replace(&def);
289✔
480
        /* ADMIN is both the auth token and user id for 'admin' user. */
481
        assert(user->def.uid == ADMIN && user->auth_token == ADMIN);
482
}
289✔
483

484
void
485
user_cache_free()
×
486
{
487
        if (user_registry)
×
488
                mh_i32ptr_delete(user_registry);
×
489
}
×
490

491
/* }}} user cache */
492

493
/** {{{ roles */
494

495
void
496
role_check(struct user *grantee, struct user *role)
506✔
497
{
498
        /*
499
         * Check that there is no loop from grantee to role:
500
         * if grantee is a role, build up a closure of all
501
         * immediate and indirect users of grantee, and ensure
502
         * the granted role is not in this set.
503
         */
504
        struct user_map transitive_closure = user_map_nil;
506✔
505
        user_map_set(&transitive_closure, grantee->auth_token);
506✔
506
        struct user_map current_layer = transitive_closure;
506✔
507
        while (! user_map_is_empty(&current_layer)) {
1,527✔
508
                /*
509
                 * As long as we're traversing a directed
510
                 * acyclic graph, we're bound to end at some
511
                 * point in a layer with no incoming edges.
512
                 */
513
                struct user_map next_layer = user_map_nil;
515✔
514
                struct user_map_iterator it;
515
                user_map_iterator_init(&it, &current_layer);
516
                struct user *user;
517
                while ((user = user_map_iterator_next(&it)))
1,034✔
518
                        user_map_union(&next_layer, &user->users);
519
                user_map_union(&transitive_closure, &next_layer);
520
                current_layer = next_layer;
515✔
521
        }
522
        /*
523
         * Check if the role is in the list of roles to which the
524
         * grantee is granted.
525
         */
526
        if (user_map_is_set(&transitive_closure,
506✔
527
                            role->auth_token)) {
506✔
528
                tnt_raise(ClientError, ER_ROLE_LOOP,
8✔
529
                          role->def.name, grantee->def.name);
530
        }
531
}
502✔
532

533
/**
534
 * Re-calculate effective grants of the linked subgraph
535
 * this user/role is a part of.
536
 */
537
void
538
rebuild_effective_grants(struct user *grantee)
3,674✔
539
{
540
        /*
541
         * Recurse over all roles to which grantee is granted
542
         * and mark them as dirty - in need for rebuild.
543
         */
544
        struct user_map_iterator it;
545
        struct user *user;
546
        struct user_map current_layer = user_map_nil;
3,674✔
547
        user_map_set(&current_layer, grantee->auth_token);
3,674✔
548
        while (!user_map_is_empty(&current_layer)) {
9,213✔
549
                struct user_map next_layer = user_map_nil;
5,539✔
550
                user_map_iterator_init(&it, &current_layer);
551
                while ((user = user_map_iterator_next(&it))) {
11,124✔
552
                        user->is_dirty = true;
5,585✔
553
                        user_map_union(&next_layer, &user->users);
5,585✔
554
                }
555
                /*
556
                 * Switch to the nodes which are not in the set
557
                 * yet.
558
                 */
559
                current_layer = next_layer;
5,539✔
560
        }
561
        /*
562
         * First, construct a subset of the transitive
563
         * closure consisting from the nodes with no
564
         * incoming edges (roles which have no granted
565
         * roles). Build their list of effective grants
566
         * from their actual grants.
567
         *
568
         * Propagate the effective grants through the
569
         * outgoing edges of the nodes, avoiding the nodes
570
         * with incoming edges from not-yet-evaluated nodes.
571
         * Eventually this process will end with a set of
572
         * nodes with no outgoing edges.
573
         */
574
        struct user_map transitive_closure = user_map_nil;
3,674✔
575
        current_layer = user_map_nil;
3,674✔
576
        user_map_set(&current_layer, grantee->auth_token);
3,674✔
577
        /*
578
         * Propagate effective privileges from the nodes
579
         * with no incoming edges to the remaining nodes.
580
         */
581
        while (! user_map_is_empty(&current_layer)) {
12,477✔
582
                struct user_map postponed = user_map_nil;
8,803✔
583
                struct user_map next_layer = user_map_nil;
8,803✔
584
                user_map_iterator_init(&it, &current_layer);
585
                while ((user = user_map_iterator_next(&it))) {
22,408✔
586
                        struct user_map indirect_edges = user->roles;
13,605✔
587
                        user_map_minus(&indirect_edges, &transitive_closure);
588
                        if (user_map_is_empty(&indirect_edges)) {
13,605✔
589
                                user_reload_privs(user);
11,051✔
590
                                user_map_union(&next_layer, &user->users);
591
                        } else {
592
                                /*
593
                                 * The user has roles whose
594
                                 * effective grants have not been
595
                                 * calculated yet. Postpone
596
                                 * evaluation of effective grants
597
                                 * of this user till these roles'
598
                                 * effective grants have been
599
                                 * built.
600
                                 */
601
                                user_map_union(&next_layer, &indirect_edges);
602
                                user_map_set(&postponed, user->auth_token);
2,554✔
603
                                user_map_set(&next_layer, user->auth_token);
2,554✔
604
                        }
605
                }
606
                user_map_minus(&current_layer, &postponed);
607
                user_map_union(&transitive_closure, &current_layer);
608
                current_layer = next_layer;
8,803✔
609
        }
610
}
3,674✔
611

612

613
/**
614
 * Update verges in the graph of dependencies.
615
 * Grant all effective privileges of the role to whoever
616
 * this role was granted to.
617
 */
618
void
619
role_grant(struct user *grantee, struct user *role)
502✔
620
{
621
        user_map_set(&role->users, grantee->auth_token);
502✔
622
        user_map_set(&grantee->roles, role->auth_token);
502✔
623
        rebuild_effective_grants(grantee);
502✔
624
}
502✔
625

626
/**
627
 * Update the role dependencies graph.
628
 * Rebuild effective privileges of the grantee.
629
 */
630
void
631
role_revoke(struct user *grantee, struct user *role)
122✔
632
{
633
        user_map_clear(&role->users, grantee->auth_token);
122✔
634
        user_map_clear(&grantee->roles, role->auth_token);
122✔
635
        rebuild_effective_grants(grantee);
122✔
636
}
122✔
637

638
void
639
priv_grant(struct user *grantee, struct priv_def *priv)
3,050✔
640
{
641
        struct access *object = access_find(priv);
3,050✔
642
        if (object == NULL)
3,050✔
643
                return;
3,050✔
644
        struct access *access = &object[grantee->auth_token];
3,050✔
645
        assert(privset_search(&grantee->privs, priv) || access->granted == 0);
646
        access->granted = priv->access;
3,050✔
647
        rebuild_effective_grants(grantee);
3,050✔
648
}
649

650
/** }}} */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc