# Занятие 02: Управление доступом / Членство в ролях

> Исходная страница в нативном Markdown: Управление доступом / Членство в ролях

---

Индекс LLMS: [llms.txt](/ru/llms.txt)

---

<!-- Generated by scripts/generate_skeleton.py. -->

> [!NOTE]
> Эта страница извлечена из официального PDF в нативный Markdown. Текст слайдов, код и комментарии сохранены в порядке чтения; исходные PDF и HTML остаются эталоном макета.
> **HTML source:** [dbs_02_access_membership.html](https://edu.postgrespro.ru/16/dbs-16/dbs_02_access_membership.html)
> **Archive:** [DBS-handouts-16.zip](https://edu.postgrespro.ru/16/DBS-handouts-16.zip)
> **Страниц PDF:** 24

## Page 1: Управление доступом / Членство в ролях {#source-page-001}

### Авторские права

© Postgres Professional, 2017–2026 Авторы: Егор Рогов, Павел Лузанов, Илья Баштанов, Алексей Береснев Фото: Олег Бартунов (монастырь Пху и пик Бхрикути, Непал)
### Использование материалов курса

Некоммерческое использование материалов курса (презентации, демонстрации) разрешается без ограничений. Коммерческое использование возможно только с письменного разрешения компании Postgres Professional. Запрещается внесение изменений в материалы курса.
### Обратная связь

Отзывы, замечания и предложения направляйте по адресу: edu@postgrespro.ru
### Отказ от ответственности

Компания Postgres Professional не несет никакой ответственности за любые повреждения и убытки, включая потерю дохода, нанесенные прямым или непрямым, специальным или случайным использованием материалов курса. Компания Postgres Professional не предоставляет каких-либо гарантий на материалы курса. Материалы курса предоставляются на основе принципа «как есть» и компания Postgres Professional не обязана предоставлять сопровождение, поддержку, обновления, расширения и изменения.

## Page 2: Темы {#source-page-002}

- Включение роли в роль
- Способы получения прав на объект
- Наследование
- Переключение на роль
- Управление включением ролей

## Page 3: Включение роли в роль {#source-page-003}

![Source slide page 3: Включение роли в роль](images/page-003.png){#source-figure-003 caption="Source slide page 3: Включение роли в роль"}

**Включение в роль Исключение из роли**
```sql
student=# GRANT dba TO alice;
student=# REVOKE dba FROM alice;
```
  - dba
  - alice
- Псевдороль PUBLIC неявно включает остальные роли

### Комментарии преподавателя

Роль можно включить в другую роль. В таком случае вторая роль выступает в качестве группы и говорят, что первая роль становится членом второй. Роль может быть включена в несколько ролей; включенная роль может, в свою очередь, включать в себя третьи роли и т. д., но циклы при этом не допускаются.

Роль может быть одновременно «пользовательской» (иметь атрибут LOGIN) и «групповой» (включать другие роли).

В кластере существует также псевдороль PUBLIC, которая неявно включает в себя все остальные роли.

<https://postgrespro.ru/docs/postgresql/16/role-membership>

## Page 4: Получение прав на объект {#source-page-004}

**Суперпользовательский доступ**
- суперпользователи не ограничены в действиях, за исключением удаления начального суперпользователя Владение
- Обладание привилегией
- Членство в группе
  - INHERIT
  - наследование прав групповой роли
  - SET
  - переключение на групповую роль
  - ADMIN
  - управление членством в групповой роли

### Комментарии преподавателя

В целом у роли есть несколько вариантов получить право на действия с определенным объектом.

Суперпользователи (роли с атрибутом SUPERUSER) — особый случай, для них проверки прав доступа не выполняются. При этом нельзя удалить начальную суперпользовательскую роль или лишить ее атрибута SUPERUSER. Начальный суперпользователь (bootstrap superuser) создается при создании кластера баз данных.

Становясь владельцем объекта, роль получает право выполнять с ним практически любые действия.

Помимо этого, у роли может возникнуть право на действие с объектом, если она получит привилегию на объект (автоматически при создании объекта или явно).

И наконец, роль может получить дополнительные права при включении в другую роль. При включении можно указать любую комбинацию параметров членства:

● INHERIT включает наследование прав групповой роли на действия с объектами;

● SET позволяет переключаться на групповую роль;

● ADMIN дает право включать в группу другие роли, изменять атрибуты членства в группе и исключать роли из группы.

Параметры INHERIT и SET появились в PostgreSQL 16.

## Page 5: Наследование {#source-page-005}

![Source slide page 5: Наследование](images/page-005.png){#source-figure-005 caption="Source slide page 5: Наследование"}

**Атрибуты не наследуются**
- Права на объекты наследуются
  - если роль входит в группу с параметром членства INHERIT
  - grp1
  - grp2
  - grp3

### Комментарии преподавателя

Если роль включена в другую роль, она может наследовать права на объекты, которыми обладает группа благодаря привилегиям или владению. Атрибуты ролей никогда не наследуются.

Роль может наследовать права на объекты от нескольких ролей, как при непосредственном включении в несколько групп, так и по цепочке включений. Наследуются не только права, которыми непосредственно обладает группа, но и права, которые она сама унаследовала.

Наследование срабатывает, когда роль включена в группу с параметром членства INHERIT. По умолчанию именно так и происходит, поскольку команда GRANT берет значение параметра INHERIT из одноименного атрибута включаемой роли, а при создании роли этот атрибут по умолчанию устанавливается. Однако параметр можно установить и явно, переопределив атрибут. Для этого надо указать в команде GRANT предложение WITH INHERIT.

## Page 6: Наследование {#source-page-006}

Зарегистрируем роли Алисы и Боба.
```sql
student=# CREATE ROLE alice LOGIN;
```
```sql
CREATE ROLE
```
```sql
student=# CREATE ROLE bob LOGIN;
```
```sql
CREATE ROLE
```
Алиса будет владельцем базы данных:
```sql
student=# CREATE DATABASE access_membership OWNER alice;
```
```sql
CREATE DATABASE
```
Алиса подключается к БД и создает таблицу в схеме public.
```sh
student$ psql -d access_membership -U alice
```
```sql
alice=> CREATE TABLE t (id int);
```
```sql
CREATE TABLE
```
Включим Боба в роль Алисы:
```text
student=# GRANT alice TO bob;
```
```text
GRANT ROLE
```
Параметры членства ролей в группах можно увидеть в системном каталоге:
```sql
student=# SELECT roleid::regrole AS group,
  member::regrole,
  grantor::regrole,
  inherit_option
FROM pg_auth_members
WHERE member = 'bob'::regrole;
```
```text
 group | member | grantor  | inherit_option
-------+--------+----------+----------------
 alice | bob    | postgres | t
(1 row)
```
Заметьте, что в сеансе student включение в роль выполнено от имени начального суперпользователя postgres. В этом случае не возникает зависимость от роли student, которая затруднит ее удаление.
Последний столбец показывает параметр членства INHERIT. Он унаследован от атрибута INHERIT роли bob, который у нее включен по умолчанию. Поэтому Боб, подключившись, сможет прочитать таблицу.
```sh
student$ psql -d access_membership -U bob
```
```sql
bob=> SELECT * FROM t;
```
```text
 id
----
(0 rows)
```
Убедиться, что привилегии Алисы унаследованы Бобом, можно и с помощью системной функции. Ее вызов с параметром USAGE показывает, наследует ли текущий пользователь привилегии роли alice:
```sql
bob=> SELECT pg_has_role(CURRENT_ROLE, 'alice', 'USAGE');
```
```text
 pg_has_role
-------------
 t
(1 row)
```
Можно задать параметр членства явно:
```text
student=# GRANT alice TO bob WITH INHERIT OPTION; -- или INHERIT TRUE
```
```text
NOTICE:  role "bob" has already been granted membership in role "alice" by role "postgres"
GRANT ROLE
```
Что будет, если лишить роль Боба атрибута INHERIT?

## Page 7: Page 7 {#source-page-007}

![Source slide page 7: Page 7](images/page-007.png){#source-figure-007 caption="Source slide page 7: Page 7"}

```sql
student=# ALTER ROLE bob NOINHERIT;
```
```sql
ALTER ROLE
```
```sql
bob=> SELECT pg_has_role(CURRENT_ROLE, 'alice', 'USAGE');
```
```text
 pg_has_role
-------------
 t
(1 row)
```
Наследование обеспечивается параметром членства INHERIT, поэтому Боб по-прежнему может читать содержимое таблицы:
```sql
bob=> SELECT * FROM t;
```
```text
 id
----
(0 rows)
```
Параметр появился в PostgreSQL 16, в более ранних версиях отмена атрибута привела бы к потере наследования.
Заметьте, что наследуются и права, которые дает владение, поэтому Боб может, например, изменить таблицу:
```sql
bob=> ALTER TABLE t SET (fillfactor=10);
```
```sql
ALTER TABLE
```

## Page 8: Переключение на роль {#source-page-008}

![Source slide page 8: Переключение на роль](images/page-008.png){#source-figure-008 caption="Source slide page 8: Переключение на роль"}

**Параметр членства SET**
- Переключение на роль Возврат к исходной роли
```text
alice=> SET ROLE dba;
alice=> RESET ROLE;
```
```text
session_user
```

### Комментарии преподавателя

Параметр членства SET позволяет выполнить команду SET ROLE и (временно) переключиться на групповую роль со всеми ее атрибутами и привилегиями на объекты. Все действия будут выполняться от имени групповой роли и создаваемые объекты будут принадлежать групповой роли. Права исходной роли при этом недоступны. Параметр появился в PostgreSQL 16, до этого переключение на групповую роль разрешалось всегда.

Имя пользователя, подключившегося к серверу, возвращает функция session\_user. А имя текущей роли можно узнать с помощью функции current\_user (или current\_role, что одно и то же).

<https://postgrespro.ru/docs/postgresql/16/functions-info#FUNCTIONS-INFO-SESSION>

## Page 9: Переключение на роль {#source-page-009}

![Source slide page 9: Переключение на роль](images/page-009.png){#source-figure-009 caption="Source slide page 9: Переключение на роль"}

Удалим параметр INHERIT членства роли bob в группе alice:
```sql
student=# REVOKE INHERIT OPTION FOR alice FROM bob;
```
```text
REVOKE ROLE
```
Членство ролей в группах и параметры членства удобно проверять с помощью метакоманды \\drg:
```text
student=# \drg bob
```
```text
            List of role grants
 Role name | Member of | Options | Grantor
-----------+-----------+---------+----------
 bob       | alice     | SET     | postgres
(1 row)
```
Привилегии отозваны немедленно:
```sql
bob=> SELECT * FROM t;
```
```text
ERROR:  permission denied for table t
```
Оставшийся параметр членства SET позволяет члену группы переключиться на групповую роль:
```text
bob=> SET ROLE alice;
```
```text
SET
```
```sql
bob=> SELECT * FROM t;
```
```text
 id
----
(0 rows)
```
Теперь все действия в сеансе Боба происходят от имени Алисы. Узнать имена текущей роли и роли, начавшей сеанс, можно с помощью функций:
```sql
bob=> SELECT current_user, session_user;
```
```text
 current_user | session_user
--------------+--------------
 alice        | bob
(1 row)
```
Боб может, например, от имени Алисы выдать себе все привилегии на таблицу:
```text
bob=> GRANT ALL ON t TO bob;
```
```text
GRANT
```
```text
bob=> \dp t
```
```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 public | t    | table | alice=arwdDxt/alice+|                   |
        |      |       | bob=arwdDxt/alice   |                   |
(1 row)
```
При переключении роль также получает атрибуты групповой роли. Добавим роли alice атрибут, разрешающий создавать базы данных:
```sql
student=# ALTER ROLE alice CREATEDB;
```
```sql
ALTER ROLE
```
Обладает ли теперь Боб, переключивший свой сеанс на роль Алисы, правом создавать базы данных?
```sql
bob=> SELECT has_database_privilege(current_catalog, 'CREATE');
```
```text
 has_database_privilege
------------------------
 t
(1 row)
```

## Page 10: Page 10 {#source-page-010}

Да, право есть.
Вернемся к исходной роли сеанса:
```text
bob=> RESET ROLE;
```
```text
RESET
```
```sql
bob=> SELECT current_user, session_user;
```
```text
 current_user | session_user
--------------+--------------
 bob          | bob
(1 row)
```
Суперпользователь может использовать другую команду для переключения на любую роль:
```text
student=# SET SESSION AUTHORIZATION bob;
```
```text
SET
```
Такой подход позволяет, например, тестировать работу под разными пользователями без необходимости подключаться к серверу под каждым из них (подробнее об этом пойдет речь в модуле «Методы аутентификации»). Обратите внимание, что имя пользователя, начавшего сеанс, скрыто:
```sql
student=# SELECT current_user, session_user;
```
```text
 current_user | session_user
--------------+--------------
 bob          | bob
(1 row)
```
Вернем сеанс суперпользователя к исходной роли:
```text
student=# RESET SESSION AUTHORIZATION;
```
```text
RESET
```
```sql
student=# SELECT current_user, session_user;
```
```text
 current_user | session_user
--------------+--------------
 student      | student
(1 row)
```

## Page 11: Управление членством {#source-page-011}

![Source slide page 11: Управление членством](images/page-011.png){#source-figure-011 caption="Source slide page 11: Управление членством"}

**Параметр членства ADMIN**
  - включение в группу
  - исключение из группы
  - изменение параметров членства
```sql
alice=> GRANT dba TO bob;
alice=> REVOKE dba FROM bob;
```
  - alice

### Комментарии преподавателя

Управлять включением ролей в группу (а также исключением ролей из группы и изменением параметров членства ролей в группе) могут те члены группы, которые имеют в ней параметр членства ADMIN.

Роль не может иметь параметр членства ADMIN сама в себе, поэтому не может управлять включением в себя других ролей.

Суперпользователи могут управлять включением любых ролей в любые.

## Page 12: Управление включением {#source-page-012}

![Source slide page 12: Управление включением](images/page-012.png){#source-figure-012 caption="Source slide page 12: Управление включением"}

Зарегистрируем еще роль для Чарли:
```sql
student=# CREATE ROLE charlie;
```
```sql
CREATE ROLE
```
Если роль входит в группу с параметром ADMIN, она может добавлять в группу другие роли:
```text
student=# GRANT alice TO bob WITH ADMIN OPTION; -- или ADMIN TRUE
```
```text
GRANT ROLE
```
```text
student=# \drg bob
```
```text
              List of role grants
 Role name | Member of |  Options   | Grantor
-----------+-----------+------------+----------
 bob       | alice     | ADMIN, SET | postgres
(1 row)
```
Обладая параметром членства ADMIN в группе Алисы, Боб теперь может включить Чарли в эту группу:
```text
bob=> GRANT alice TO charlie;
```
```text
GRANT ROLE
```
```text
bob=> \drg charlie
```
```text
              List of role grants
 Role name | Member of |   Options    | Grantor
-----------+-----------+--------------+---------
 charlie   | alice     | INHERIT, SET | bob
(1 row)
```
По умолчанию параметр SET устанавливается, а параметр INHERIT, как мы уже видели, наследуется от атрибута INHERIT включаемой роли. Параметр членства ADMIN в группе Алисы дает Бобу право изменять параметры членства других ролей в этой группе:
```sql
bob=> REVOKE INHERIT OPTION FOR alice FROM charlie;
```
```text
REVOKE ROLE
```
```text
bob=> \drg charlie
```
```text
            List of role grants
 Role name | Member of | Options | Grantor
-----------+-----------+---------+---------
 charlie   | alice     | SET     | bob
(1 row)
```
Включать в свою группу другие роли Боб не может:
```text
bob=> GRANT bob TO charlie;
```
```text
ERROR:  permission denied to grant role "bob"
DETAIL:  Only roles with the ADMIN option on role "bob" may grant this role.
```

## Page 13: Каскадное исключение {#source-page-013}

![Source slide page 13: Каскадное исключение](images/page-013.png){#source-figure-013 caption="Source slide page 13: Каскадное исключение"}

**Лишение членства в роли с параметром ADMIN**
```sql
student=# REVOKE alice FROM bob CASCADE;
```
  - bob

### Комментарии преподавателя

Когда пользователь лишается членства в роли, которым он обладал с параметром ADMIN, членства должны лишиться и другие роли, которым оно было предоставлено этим пользователем. В этом случае в команде REVOKE необходимо указать ключевое слово CASCADE, иначе будет зафиксирована ошибка.

Такое лишение членства действует рекурсивно, точно так же, как при отзыве права передачи привилегии GRANT OPTION, которое рассматривалось в предыдущей теме.

<https://postgrespro.ru/docs/postgresql/16/sql-revoke>

## Page 14: Каскадное исключение {#source-page-014}

Боб был включен в группу Алисы с правом управления и воспользовался им:
```text
student=# \drg bob|charlie
```
```text
              List of role grants
 Role name | Member of |  Options   | Grantor
-----------+-----------+------------+----------
 bob       | alice     | ADMIN, SET | postgres
 charlie   | alice     | SET        | bob
(2 rows)
```
Поэтому нельзя просто так исключить его из группы:
```sql
student=# REVOKE alice FROM bob;
```
```text
ERROR:  dependent privileges exist
HINT:  Use CASCADE to revoke them too.
```
Но можно, используя ключевое слово CASCADE, удалить Боба из группы вместе со всеми, кого тот успел включить в группу Алисы:
```sql
student=# REVOKE alice FROM bob CASCADE;
```
```text
REVOKE ROLE
```
```text
student=# \drg bob|charlie
```
```text
            List of role grants
 Role name | Member of | Options | Grantor
-----------+-----------+---------+---------
(0 rows)
```
Можно было бы отозвать у Боба право управления, не исключая его из группы Алисы, но при этом все равно пришлось бы указать CASCADE.
Роль можно удалить, если она не владеет объектами. Просто так удалить Алису и Боба не получится:
```sql
student=# DROP ROLE alice;
```
```text
ERROR:  role "alice" cannot be dropped because some objects depend on it
DETAIL:  owner of database access_membership
1 object in database access_membership
```
```sql
student=# DROP ROLE bob;
```
```text
ERROR:  role "bob" cannot be dropped because some objects depend on it
DETAIL:  1 object in database access_membership
```
Можно удалить объекты, принадлежащие роли, если они не нужны:
```sql
student=# \c access_membership
```
```text
You are now connected to database "access_membership" as user "student".
```
```sql
student=# DROP OWNED BY bob;
```
```sql
DROP OWNED
```
Если же удалить объекты нельзя, можно передать их другому владельцу:
```text
student=# REASSIGN OWNED BY alice TO student;
```
```text
REASSIGN OWNED
```
В общем случае (без прав суперпользователя) для смены владельца командой REASSIGN OWNED BY или ALTER ... OWNER TO нужен параметр членства SET в роли нового владельца.
```sql
student=# DROP ROLE alice, bob, charlie;
```
```sql
DROP ROLE
```

## Page 15: Итоги {#source-page-015}

**Роли можно включать друг в друга**
- Права на объекты приобретаются за счет привилегий, владения и членства в группах
- Параметры членства определяют порядок передачи прав при включении

## Page 16: Практика {#source-page-016}

1. Создайте роль для Алисы, включив ее в суперпользователь-скую роль с параметром SET. Все дальнейшие действия выполняются в сеансе Алисы.
- 2. Создайте пользователя Боб — члена группы readers — и пользователя Чарли — члена группы writers. Оба включения выполнены с параметром INHERIT (без SET).
- 3. Создайте таблицу в отдельной схеме, выдайте роли readers привилегию на ее чтение, а роли writers — на изменение.
- 4. Проверьте, могут ли Алиса, Боб и Чарли читать и менять строки таблицы.

## Page 17: 1. Роль Алисы {#source-page-017}

```sql
student=# CREATE USER alice NOINHERIT IN ROLE student;
```
```sql
CREATE ROLE
```
Эта команда эквивалентна двум следующим:
```sql
CREATE ROLE alice LOGIN NOINHERIT;
GRANT student TO alice;
```
```sql
student=# \c - alice
```
```text
You are now connected to database "student" as user "alice".
```
```text
alice=> SET ROLE student;
```
```text
SET
```
### 2. Пользователи и группы
Создадим группы readers и writers.
```sql
alice=> CREATE ROLE readers;
```
```sql
CREATE ROLE
```
```sql
alice=> CREATE ROLE writers;
```
```sql
CREATE ROLE
```
Боб — читатель.
```sql
alice=> CREATE USER bob;
```
```sql
CREATE ROLE
```
```text
alice=> GRANT readers TO bob WITH SET FALSE;
```
```text
GRANT ROLE
```
Чарли — писатель.
```sql
alice=> CREATE USER charlie;
```
```sql
CREATE ROLE
```
```text
alice=> GRANT writers TO charlie WITH SET FALSE;
```
```text
GRANT ROLE
```
Вот что получилось:
```text
alice=> \drg
```
```text
            List of role grants
 Role name | Member of | Options | Grantor
-----------+-----------+---------+----------
 alice     | student   | SET     | postgres
 bob       | readers   | INHERIT | postgres
 charlie   | writers   | INHERIT | postgres
(3 rows)
```
### 3. База данных, схема и таблица
```sql
alice=> CREATE DATABASE access_membership OWNER alice;
```
```sql
CREATE DATABASE
```
```sql
alice=> \c access_membership
```
```text
You are now connected to database "access_membership" as user "alice".
```
```sql
alice=> CREATE SCHEMA app;
```
```sql
CREATE SCHEMA
```
```sql
alice=> CREATE TABLE app.tab(who text DEFAULT current_user);
```

## Page 18: Page 18 {#source-page-018}

```sql
CREATE TABLE
```
Привилегии:
```text
alice=> GRANT USAGE ON SCHEMA app TO readers, writers;
```
```text
GRANT
```
```sql
alice=> GRANT SELECT ON TABLE app.tab TO readers;
```
```text
GRANT
```
```sql
alice=> GRANT INSERT, UPDATE, DELETE ON TABLE app.tab TO writers;
```
```text
GRANT
```
### 4. Права на таблицу
```text
alice=> \dp app.tab
```
```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 app    | tab  | table | alice=arwdDxt/alice+|                   |
        |      |       | readers=r/alice    +|                   |
        |      |       | writers=awd/alice   |                   |
(1 row)
```
Алиса может читать и менять строки, поскольку является владельцем:
```sql
alice=> INSERT INTO app.tab DEFAULT VALUES;
```
```sql
INSERT 0 1
```
```sql
alice=> SELECT * FROM app.tab;
```
```text
  who
-------
 alice
(1 row)
```
```sql
alice=> \c - bob
```
```text
You are now connected to database "access_membership" as user "bob".
```
Боб может читать:
```sql
bob=> SELECT * FROM app.tab;
```
```text
  who
-------
 alice
(1 row)
```
Но не может менять:
```sql
bob=> INSERT INTO app.tab DEFAULT VALUES;
```
```text
ERROR:  permission denied for table tab
```
```sql
bob=> \c - charlie
```
```text
You are now connected to database "access_membership" as user "charlie".
```
Чарли может менять строки:
```sql
charlie=> INSERT INTO app.tab DEFAULT VALUES;
```
```sql
INSERT 0 1
```
Но не читать их:
```sql
charlie=> SELECT * FROM app.tab;
```
```text
ERROR:  permission denied for table tab
```
### Уборка
```sql
charlie=> \c student student
```

## Page 19: Page 19 {#source-page-019}

```text
You are now connected to database "student" as user "student".
```
```sql
student=# DROP DATABASE access_membership WITH (FORCE);
```
```sql
DROP DATABASE
```
```sql
student=# DROP ROLE alice, bob, charlie, readers, writers;
```
```sql
DROP ROLE
```

## Page 20: Практика+ {#source-page-020}

Изучите способы получения метаинформации.
- 1. Создайте роли, схему и таблицу, как это сделано в Практике.
- 2. Получите: список объектов Алисы; права пользователей на эти объекты; список ролей, имеющих право чтения таблицы; информацию о включении ролей в группы.
- 3. Может ли пользователь Дейв, не имеющий доступа к объектам, увидеть эту метаинформацию?

### Комментарии преподавателя

2, 3. Для получения метаинформации можно использовать следующие источники:

● системный каталог <https://postgrespro.ru/docs/postgresql/16/catalogs>;

● системные информационные функции <https://postgrespro.ru/docs/postgresql/16/functions-info>;

● метакоманды psql <https://postgrespro.ru/docs/postgresql/16/app-psql#:~:text=удаляет%20п> еред%20отправкой.-,Метакоманды,-Всё%2C%20что%20вводится

● информационную схему <https://postgrespro.ru/docs/postgresql/16/information-schema>.

## Page 21: 1. Роли, схема, таблица {#source-page-021}

Роль Алисы.
```sql
student=# CREATE USER alice NOINHERIT IN ROLE student;
```
```sql
CREATE ROLE
```
```sql
student=# \c - alice
```
```text
You are now connected to database "student" as user "alice".
```
```text
alice=> SET ROLE student;
```
```text
SET
```
Группы readers и writers.
```sql
alice=> CREATE ROLE readers;
```
```sql
CREATE ROLE
```
```sql
alice=> CREATE ROLE writers;
```
```sql
CREATE ROLE
```
Боб — читатель.
```sql
alice=> CREATE USER bob;
```
```sql
CREATE ROLE
```
```text
alice=> GRANT readers TO bob WITH SET FALSE;
```
```text
GRANT ROLE
```
Чарли — писатель.
```sql
alice=> CREATE USER charlie;
```
```sql
CREATE ROLE
```
```text
alice=> GRANT writers TO charlie WITH SET FALSE;
```
```text
GRANT ROLE
```
Вот что получилось:
```text
alice=> \drg
```
```text
            List of role grants
 Role name | Member of | Options | Grantor
-----------+-----------+---------+----------
 alice     | student   | SET     | postgres
 bob       | readers   | INHERIT | postgres
 charlie   | writers   | INHERIT | postgres
(3 rows)
```
База данных и таблица.
```sql
alice=> CREATE DATABASE access_membership OWNER alice;
```
```sql
CREATE DATABASE
```
```sql
alice=> \c access_membership
```
```text
You are now connected to database "access_membership" as user "alice".
```
```sql
alice=> CREATE SCHEMA app;
```
```sql
CREATE SCHEMA
```
```sql
alice=> CREATE TABLE app.tab(who text DEFAULT current_user);
```
```sql
CREATE TABLE
```
Привилегии.
```text
alice=> GRANT USAGE ON SCHEMA app TO readers, writers;
```
```text
GRANT
```

## Page 22: 2. Метаинформация {#source-page-022}

```sql
alice=> GRANT SELECT ON TABLE app.tab TO readers;
```
```text
GRANT
```
```sql
alice=> GRANT INSERT, UPDATE, DELETE ON TABLE app.tab TO writers;
```
```text
GRANT
```
Получим информацию от имени суперпользователя.
```sql
alice=> \c - student
```
```text
You are now connected to database "access_membership" as user "student".
```
В системном каталоге можно увидеть список схем:
```sql
student=# SELECT * FROM pg_namespace;
```
```text
  oid  |      nspname       | nspowner |                            nspacl
-------+--------------------+----------+--------------------------------------------------
-------------
    99 | pg_toast           |       10 |
    11 | pg_catalog         |       10 | {postgres=UC/postgres,=U/postgres}
  2200 | public             |     6171 |
{pg_database_owner=UC/pg_database_owner,=U/pg_database_owner}
 13296 | information_schema |       10 | {postgres=UC/postgres,=U/postgres}
 16395 | app                |    16386 | {alice=UC/alice,readers=U/alice,writers=U/alice}
(5 rows)
```
А также список объектов Алисы с привилегиями на них:
```sql
student=# SELECT oid, relname, relacl
FROM pg_class
WHERE relowner = 'alice'::regrole;
```
```text
  oid  |       relname        |                         relacl
-------+----------------------+---------------------------------------------------------
 16400 | pg_toast_16396       |
 16401 | pg_toast_16396_index |
 16396 | tab                  | {alice=arwdDxt/alice,readers=r/alice,writers=awd/alice}
(3 rows)
```
Получить список ролей, имеющих право чтения таблицы, можно с помощью функции:
```sql
student=# SELECT rolname
FROM pg_roles
WHERE has_table_privilege(rolname, 'app.tab', 'SELECT');
```
```text
     rolname
------------------
 pg_read_all_data
 student
 postgres
 alice
 readers
 bob
(6 rows)
```
А сведения о включении ролей — используя другую функцию:
```sql
student=# SELECT rolname
FROM pg_roles
WHERE pg_has_role('bob', rolname, 'MEMBER,USAGE');
```
```text
 rolname
---------
 readers
 bob
(2 rows)
```
Более подробную информацию о включении дает метакоманда psql:
```text
student=# \drg
```

## Page 23: Page 23 {#source-page-023}

```text
            List of role grants
 Role name | Member of | Options | Grantor
-----------+-----------+---------+----------
 alice     | student   | SET     | postgres
 bob       | readers   | INHERIT | postgres
 charlie   | writers   | INHERIT | postgres
(3 rows)
```
В некоторых случаях полезна информационной схема: например, доступные текущему пользователю таблицы показывает представление tables.
```sql
student=# SELECT table_schema, table_name, string_agg(privilege_type,',') AS privs
FROM information_schema.tables
  NATURAL JOIN information_schema.table_privileges
WHERE table_schema = 'app'
GROUP BY 1,2
ORDER BY 1,2 \gx
```
```sql
-[ RECORD 1
]+------------------------------------------------------------------------------------
table_schema | app
table_name   | tab
privs        |
INSERT,SELECT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER,SELECT,INSERT,UPDATE,DELETE
```
### 3. Обычный пользователь
```sql
student=# CREATE USER dave;
```
```sql
CREATE ROLE
```
```sql
student=# \c - dave
```
```text
You are now connected to database "access_membership" as user "dave".
```
Дейв не имеет доступа к схеме и таблице:
```sql
dave=> SELECT * FROM app.tab;
```
```sql
ERROR:  permission denied for schema app
LINE 1: SELECT * FROM app.tab;
                      ^
```
Однако он видит список всех таблиц:
```sql
dave=> SELECT oid, relname, relacl
FROM pg_class
WHERE relowner = 'alice'::regrole;
```
```text
  oid  |       relname        |                         relacl
-------+----------------------+---------------------------------------------------------
 16400 | pg_toast_16396       |
 16401 | pg_toast_16396_index |
 16396 | tab                  | {alice=arwdDxt/alice,readers=r/alice,writers=awd/alice}
(3 rows)
```
Список ролей, имеющих право чтения таблицы, тоже доступен (используем oid вместо имени, так как доступа к схеме нет):
```sql
dave=> SELECT rolname
FROM pg_roles
WHERE has_table_privilege(rolname, 16396::regclass, 'SELECT');
```
```text
     rolname
------------------
 pg_read_all_data
 student
 postgres
 alice
 readers
 bob
(6 rows)
```
Пользователь может получить и информацию о включении ролей:
```sql
dave=> SELECT rolname
FROM pg_roles
WHERE pg_has_role('bob', rolname, 'MEMBER,USAGE');
```

## Page 24: Page 24 {#source-page-024}

```text
 rolname
---------
 readers
 bob
(2 rows)
```
```text
dave=> \drg
```
```text
            List of role grants
 Role name | Member of | Options | Grantor
-----------+-----------+---------+----------
 alice     | student   | SET     | postgres
 bob       | readers   | INHERIT | postgres
 charlie   | writers   | INHERIT | postgres
(3 rows)
```
### Уборка
```sql
dave=> \c student student
```
```text
You are now connected to database "student" as user "student".
```
```sql
student=# DROP DATABASE access_membership WITH (FORCE);
```
```sql
DROP DATABASE
```
```sql
student=# DROP ROLE alice, bob, charlie, dave, readers, writers;
```
```sql
DROP ROLE
```
