# Занятие 01: Управление доступом / Роли и привилегии

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

---

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

---

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

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

## 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}

**Роль — учетная запись СУБД и набор прав доступа**
- Определяется на уровне кластера
- Не связана с учетной записью ОС
  - хотя многие программы используют имя пользователя ОС
  - как имя роли по умолчанию

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

PostgreSQL использует вариант ролевой модели доступа (Role-Based Access Control, RBAC). В классической модели выделяются пользователи (учетные записи) и назначаемые им роли, которые определяют права пользователей. В PostgreSQL оба этих понятия объединены и называются ролью. Таким образом, роль — это и учетная запись СУБД, и набор прав доступа.

Роли задаются на уровне кластера: для всех баз данных набор ролей одинаков. Клиент всегда подключается к базе данных от имени некоторой роли. Формально роли (как учетные записи СУБД) никак не связаны с учетными записями операционной системы, хотя многие программы это предполагают, выбирая значения по умолчанию. Например, psql, запущенный от имени пользователя ОС student, автоматически использует одноименную роль student (если в ключах утилиты не указана какая-либо другая роль).

<https://postgrespro.ru/docs/postgresql/16/database-roles>

## Page 4: Атрибуты {#source-page-004}

**Права роли, не связанные с определенными объектами**
  - LOGIN
  - возможность подключения
  - SUPERUSER
  - суперпользователь
  - CREATEDB
  - возможность создавать базы данных
  - CREATEROLE
  - возможность создавать роли
  - REPLICATION
  - использование протокола репликации
  - и другие

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

Роль обладает атрибутами, которые определяют права доступа, не связанные с определенными объектами баз данных.

Обычно атрибуты имеют два варианта, например, CREATEDB (дает право на создание БД) и NOCREATEDB (не дает такого права).

Если у роли есть атрибут LOGIN, она имеет право подключаться к серверу, то есть является пользователем. Атрибут NOLOGIN запрещает подключение, то есть роль может использоваться только как набор прав (который можно передать другим пользователям, о чем будет говориться в следующей теме «Членство в ролях»).

На слайде перечислены некоторые атрибуты.

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

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

## Page 5: Роли и атрибуты {#source-page-005}

Сейчас в кластере две пользовательские роли:
```text
student=# \du
```
```sql
                             List of roles
 Role name |                         Attributes
-----------+------------------------------------------------------------
 postgres  | Superuser, Create role, Create DB, Replication, Bypass RLS
 student   | Superuser, Create role, Create DB, Replication, Bypass RLS
```
postgres — начальный суперпользователь, создается при инициализации кластера, удалить его нельзя, student — тоже суперпользователь, создан для курса, большинство действий в демонстрациях выполняется от его имени;
Создадим базу данных:
```sql
student=# CREATE DATABASE access_roles;
```
```sql
CREATE DATABASE
```
```sql
student=# \c access_roles
```
```text
You are now connected to database "access_roles" as user "student".
```
Создадим роль для пользователя Алисы с несколькими атрибутами:
```sql
student=# CREATE ROLE alice
  LOGIN
  CONNECTION LIMIT 1;
```
```sql
CREATE ROLE
```
```text
student=# \du alice
```
```text
      List of roles
 Role name |  Attributes
-----------+--------------
 alice     | 1 connection
```
Атрибуты роли alice:
LOGIN — имеет право подключаться, то есть является пользовательской; CONNECTION LIMIT 1 — не более одного соединения.
Обратите внимание, что наличие атрибута LOGIN не показывается.
Алиса может подключиться к базе данных:
```sh
student$ psql -U alice -d access_roles
```
```sql
alice=> \conninfo
```
```text
You are connected to database "access_roles" as user "alice" via socket in
"/var/run/postgresql" at port "5432".
```
Но это подключение — единственное возможное для нее:
```sh
student$ psql -U alice -d access_roles -c 'SELECT now();'
```
```text
psql: error: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed:
FATAL:  too many connections for role "alice"
```
Алиса не имеет атрибута CREATEROLE, поэтому она не может создать роль для Боба:
```sql
alice=> CREATE ROLE bob;
```
```sql
ERROR:  permission denied to create role
DETAIL:  Only roles with the CREATEROLE attribute may create roles.
```
Придется использовать сеанс роли с атрибутом SUPERUSER.
```text
student=# \du student
```

## Page 6: Page 6 {#source-page-006}

```sql
                             List of roles
 Role name |                         Attributes
-----------+------------------------------------------------------------
 student   | Superuser, Create role, Create DB, Replication, Bypass RLS
```
```sql
student=# CREATE USER bob; -- то же, что CREATE ROLE ... LOGIN
```
```sql
CREATE ROLE
```
Роль для Боба создана, теперь он может подключаться:
```sh
student$ psql -U bob -d access_roles -c '\conninfo'
```
```text
You are connected to database "access_roles" as user "bob" via socket in
"/var/run/postgresql" at port "5432".
```
Существующие роли можно изменять. Например, student может отобрать у Боба право входа:
```sql
student=# ALTER ROLE bob NOLOGIN;
```
```sql
ALTER ROLE
```
Метакоманда \\du не показывает атрибут LOGIN, но показывает его отсутствие:
```text
student=# \du bob
```
```text
      List of roles
 Role name |  Attributes
-----------+--------------
 bob       | Cannot login
```
Вернем Бобу право подключаться к базам данных кластера:
```sql
student=# ALTER ROLE bob LOGIN;
```
```sql
ALTER ROLE
```
Теперь он снова может начать сеанс:
```sh
student$ psql -U bob -d access_roles
```
А Алису освободим от ограничения на количество сеансов:
```sql
student=# ALTER ROLE alice CONNECTION LIMIT -1;
```
```sql
ALTER ROLE
```
```text
student=# \du alice|bob
```
```text
     List of roles
 Role name | Attributes
-----------+------------
 alice     |
 bob       |
```

## Page 7: Привилегия и владелец {#source-page-007}

**Привилегия**
- дает роли право на определенные действия с объектом Владелец объекта
  - изначально — создавшая его роль, может быть переназначен
  - получает все привилегии для этого объекта
  - имеет право на действия, не регулируемые привилегиями

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

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

Когда роль создает какой-либо объект, она по умолчанию становится его владельцем. Для некоторых объектов команда создания позволяет указать другого владельца. При необходимости владельца объекта можно сменить командой ALTER с предложением OWNER TO.

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

В дополнение к таким неотъемлемым правам, при создании объекта владелец получает все возможные привилегии на него. Формально эти привилегии могут быть отозваны суперпользователем, однако владелец всегда может их вернуть, поскольку имеет право на выдачу привилегий, в том числе и самому себе.

## Page 8: Владелец {#source-page-008}

Создадим схему alice, сразу назначив владельца:
```sql
student=# CREATE SCHEMA alice AUTHORIZATION alice;
```
```sql
CREATE SCHEMA
```
```text
student=# \dn alice
```
```text
List of schemas
 Name  | Owner
-------+-------
 alice | alice
(1 row)
```
Алиса может создавать объекты в своей схеме:
```sql
alice=> CREATE TABLE t AS SELECT 'Таблица Алисы' msg;
```
```sql
SELECT 1
```
```sql
student=# \dt *.t
```
```text
       List of relations
 Schema | Name | Type  | Owner
--------+------+-------+-------
 alice  | t    | table | alice
(1 row)
```
Как владелец, Алиса имеет право на все действия с таблицей, например, построить индекс:
```sql
alice=> CREATE INDEX ON t(msg);
```
```sql
CREATE INDEX
```
Поменяем владельца таблицы:
```sql
student=# ALTER TABLE alice.t OWNER TO student;
```
```sql
ALTER TABLE
```
```sql
student=# \dt *.t
```
```text
        List of relations
 Schema | Name | Type  |  Owner
--------+------+-------+---------
 alice  | t    | table | student
(1 row)
```
Теперь Алиса не может перестроить индекс:
```text
alice=> REINDEX TABLE t;
```
```text
ERROR:  must be owner of table t
```
Это право привилегиями не регулируется.

## Page 9: Привилегии {#source-page-009}

![Source slide page 9: Привилегии](images/page-009.png){#source-figure-009 caption="Source slide page 9: Привилегии"}

**Табличные пространства, базы данных, схемы**

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

Список возможных привилегий отличается для объектов различных типов. Привилегии для основных объектов приведены на этом и следующих слайдах.

Для табличных пространств есть привилегия CREATE, разрешающая создание объектов в этом пространстве.

Для баз данных привилегия CREATE разрешает создавать схемы в этой БД, а привилегия CONNECT разрешает подключение к этой базе данных.

Привилегия USAGE схемы разрешает обращаться к объектам в этой схеме, а для создания объектов в ней требуется привилегия CREATE.

Привилегия на создание временных таблиц вынесена на уровень БД (TEMPORARY), поскольку точное имя схемы для временных объектов заранее неизвестно.

<https://postgrespro.ru/docs/postgresql/16/ddl-priv#PRIVILEGES-SUMMARY-TABLE>

## Page 10: Привилегии {#source-page-010}

**Таблицы и представления**
  - SELECT
  - чтение данных
  - INSERT
  - вставка строк
  - можно на уровне столбцов
  - UPDATE
  - изменение строк
  - REFERENCES
  - внешний ключ (для таблиц)
  - DELETE
  - удаление строк
  - TRUNCATE
  - опустошение (для таблиц)
  - TRIGGER
  - создание триггеров
- Последовательности
  - SELECT
  - currval
  - UPDATE
  - nextval
  - setval
  - USAGE
  - currval
  - nextval

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

Больше всего привилегий существует для таблиц и представлений, они показаны на слайде. Часть привилегий можно выдать не только для всего отношения, но и для отдельных столбцов. В PostgreSQL 17 добавлена привилегия MAINTAIN, разрешающая выполнять с таблицей служебные операции, такие как очистка, анализ, перестроение индекса и другие.

Возможно, несколько неожиданный набор привилегий имеют последовательности. Выбирая нужные, можно разрешить или запретить доступ к трем управляющим функциям.

<https://postgrespro.ru/docs/postgresql/16/ddl-priv>

## Page 11: Привилегии {#source-page-011}

**Подпрограммы**
  - EXECUTE
  - выполнение
- Типы данных, языки, ...
  - USAGE
  - использование
- Параметры
  - ALTER SYSTEM
  - изменение в кластере
  - SET
  - изменение в сеансе

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

Набор привилегий для других объектов довольно прост.

Для подпрограмм единственная привилегия EXECUTE разрешает выполнение функции или процедуры.

Для таких объектов, как тип данных, домен, язык, обертка сторонних данных и внешний сервер, есть единственная привилегия USAGE, разрешающая использовать объект. Эти виды объектов изучаются в курсе DEV2 «Разработка серверной части PostgreSQL. Расширенный курс».

Для параметров конфигурации привилегия SET дает право задать значение на уровне сеанса, а привилегия ALTER SYSTEM — в postgresql.auto.conf командой ALTER SYSTEM.

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

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

**Выдача привилегии**
  - alice
  - bob
- Отзыв привилегии
  - alice
  - bob

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

Привилегии выдаются командой GRANT, а отзываются командой REVOKE. Для выдачи и отзыва привилегии на объект нужно иметь на это право. Изначально такое право есть у владельца.

Синтаксис команд GRANT и REVOKE достаточно сложен и позволяет указывать как отдельные, так и все возможные привилегии; как отдельные объекты, так и группы объектов, входящие в определенные схемы и т. п. Можно указать одну или несколько ролей, а также использовать ключевое слово PUBLIC; в последнем случае команда действует на все роли кластера, в том числе и будущие.

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

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

## Page 13: Привилегии {#source-page-013}

![Source slide page 13: Привилегии](images/page-013.png){#source-figure-013 caption="Source slide page 13: Привилегии"}

Алиса создает пару таблиц:
```sql
alice=> CREATE TABLE t1 AS SELECT 1 AS n;
```
```sql
SELECT 1
```
```sql
alice=> CREATE TABLE t2(n integer,m integer);
```
```sql
CREATE TABLE
```
А Боб пытается обратиться к таблицам Алисы:
```sql
bob=> SELECT * FROM alice.t1;
```
```sql
ERROR:  permission denied for schema alice
LINE 1: SELECT * FROM alice.t1;
                      ^
```
Не получилось: у Боба нет привилегий на схему Алисы, по умолчанию их имеет только владелец:
```text
alice=> \dn+ alice
```
```text
                 List of schemas
 Name  | Owner | Access privileges | Description
-------+-------+-------------------+-------------
 alice | alice |                   |
(1 row)
```
Метакоманды не отображают начальный набор привилегий, выданных при создании объекта, но его можно увидеть с помощью функции:
```sql
student=# SELECT acldefault(
  'n',             -- тип объекта (n — схема)
  'alice'::regrole -- OID владельца
);
```
```text
    acldefault
------------------
 {alice=UC/alice}
(1 row)
```
Привилегии выводятся как массив значений в формате: роль=привилегии/кем\_предоставлены. При создании объекта владелец получает привилегии от самого себя.
Привилегии обозначаются одной буквой. Для схем:
U = usage — право обращаться к объектам схемы; C = create — право создавать объекты в схеме.
Алиса выдает Бобу привилегию на доступ к объектам схемы:
```text
alice=> GRANT USAGE ON SCHEMA alice TO bob;
```
```text
GRANT
```
Теперь метакоманда покажет выданные привилегии:
```text
alice=> \dn+ alice
```
```text
                 List of schemas
 Name  | Owner | Access privileges | Description
-------+-------+-------------------+-------------
 alice | alice | alice=UC/alice   +|
       |       | bob=U/alice       |
(1 row)
```
Боб повторяет попытку обратиться к таблице alice.t1:
```sql
bob=> SELECT * FROM alice.t1;
```
```text
ERROR:  permission denied for table t1
```
Теперь Бобу не хватает прав на чтение таблицы. Алиса выдает привилегию:
```sql
alice=> GRANT SELECT ON t1 TO bob;
```

## Page 14: Page 14 {#source-page-014}

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

```text
GRANT
```
Посмотрим, какие привилегии установлены:
```text
alice=> \dp t1
```
```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 alice  | t1   | table | alice=arwdDxt/alice+|                   |
        |      |       | bob=r/alice         |                   |
(1 row)
```
Видим, что привилегии Бобу предоставила Алиса. Обозначения привилегий для таблиц (не вполне очевидные):
a = insert; r = select; w = update; d = delete; D = truncate; x = reference; t = trigger.
Теперь Боб может прочитать содержимое таблицы:
```sql
bob=> SELECT * FROM alice.t1;
```
```text
 n
---
 1
(1 row)
```
Но, например, добавить строку в таблицу он не сможет:
```sql
bob=> INSERT INTO alice.t1 VALUES (42);
```
```text
ERROR:  permission denied for table t1
```
Некоторые привилегии можно выдать на определенные столбцы:
```sql
alice=> GRANT INSERT(n,m) ON t2 TO bob;
```
```text
GRANT
```
```sql
alice=> GRANT SELECT(m) ON t2 TO bob;
```
```text
GRANT
```
```text
alice=> \dp t2
```
```text
                            Access privileges
 Schema | Name | Type  | Access privileges | Column privileges | Policies
--------+------+-------+-------------------+-------------------+----------
 alice  | t2   | table |                   | n:               +|
        |      |       |                   |   bob=a/alice    +|
        |      |       |                   | m:               +|
        |      |       |                   |   bob=ar/alice    |
(1 row)
```
Теперь Боб может добавлять строки в t2:
```sql
bob=> INSERT INTO alice.t2(n,m) VALUES (1,2);
```
```sql
INSERT 0 1
```
А читать сможет только один столбец:
```sql
bob=> SELECT * FROM alice.t2;
```
```text
ERROR:  permission denied for table t2
```
```sql
bob=> SELECT m FROM alice.t2;
```
```text
 m
---
 2
(1 row)
```
Если необходимо, Алиса может выдать Бобу все привилегии, не перечисляя их явно:
```text
alice=> GRANT ALL ON t1 TO bob;
```

## Page 15: Page 15 {#source-page-015}

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

```text
GRANT
```
```text
alice=> \dp t1
```
```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 alice  | t1   | table | alice=arwdDxt/alice+|                   |
        |      |       | bob=arwdDxt/alice   |                   |
(1 row)
```
Теперь Бобу доступны все действия, например, удаление строк:
```sql
bob=> DELETE FROM alice.t1;
```
```sql
DELETE 1
```
Однако удалить таблицу он не может, поскольку привилегии для этого не существует:
```sql
bob=> DROP TABLE alice.t1;
```
```text
ERROR:  must be owner of table t1
```

## Page 16: Право передачи {#source-page-016}

![Source slide page 16: Право передачи](images/page-016.png){#source-figure-016 caption="Source slide page 16: Право передачи"}

**Выдача привилегии с правом передачи**
  - alice

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

При выдаче привилегии можно передать роли право дальнейшей передачи (и отзыва) этой привилегии с помощью команды GRANT с предложением WITH GRANT OPTION.

Таким образом могут образовываться цепочки передачи привилегий; переданные привилегии считаются зависимыми от передавшей их роли. Причем одна привилегия может быть получена ролью несколько раз от разных ролей (как ролью charlie на слайде).

## Page 17: Право передачи {#source-page-017}

![Source slide page 17: Право передачи](images/page-017.png){#source-figure-017 caption="Source slide page 17: Право передачи"}

**Отзыв привилегии вместе с правом передачи**
  - alice
- Отзыв только права передачи
  - alice

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

Команда REVOKE отзывает привилегию вместе с правом передачи. Роль может отозвать привилегию только у той роли, которой она непосредственно выдала эту привилегию. Например, в ситуации, показанной на слайде, alice не может отозвать привилегию у dave.

Однако при отзыве привилегии у роли bob также должны быть отозваны и все зависимые привилегии: отзыв коснется всех ролей, которым эта привилегия досталась через Боба. Для этого надо указать ключевое слово CASCADE (без него команда завершится ошибкой, если Боб успел передать привилегию кому-либо).

Право передачи можно отозвать, не отзывая у роли саму привилегию. Это выполняется с помощью команды REVOKE GRANT OPTION FOR. Слово CASCADE имеет здесь такое же значение, как и при отзыве привилегии: поскольку Боб теперь не может передавать привилегию другим ролям, все зависимые привилегии будут отозваны.

## Page 18: Право передачи {#source-page-018}

![Source slide page 18: Право передачи](images/page-018.png){#source-figure-018 caption="Source slide page 18: Право передачи"}

Создадим третью роль, для Чарли.
```sql
student=# CREATE ROLE charlie LOGIN;
```
```sql
CREATE ROLE
```
У Боба есть полный доступ к t1, принадлежащей Алисе:
```text
bob=> \dp alice.t1
```
```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 alice  | t1   | table | alice=arwdDxt/alice+|                   |
        |      |       | bob=arwdDxt/alice   |                   |
(1 row)
```
Но Боб не может передать свою привилегию Чарли:
```sql
bob=> GRANT SELECT ON alice.t1 TO charlie;
```
```text
WARNING:  no privileges were granted for "t1"
GRANT
```
Заметьте, что команда завершилась успешно, однако при этом ничего не сделала. В подобных случаях PostgreSQL может выдавать предупреждение, но рассчитывать на него не стоит.
Чтобы Боб мог передать привилегию, Алиса, владелец таблицы, должна выдать Бобу разрешение.
```sql
alice=> GRANT SELECT,UPDATE ON alice.t1 TO bob WITH GRANT OPTION;
```
```text
GRANT
```
```text
alice=> \dp alice.t1
```
```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 alice  | t1   | table | alice=arwdDxt/alice+|                   |
        |      |       | bob=ar*w*dDxt/alice |                   |
(1 row)
```
Звездочки справа от символов привилегий показывают право передачи.
Теперь Боб может поделиться с Чарли привилегиями, в том числе и правом передачи:
```sql
bob=> GRANT SELECT ON alice.t1 TO charlie WITH GRANT OPTION;
```
```text
GRANT
```
```sql
bob=> GRANT UPDATE ON alice.t1 TO charlie;
```
```text
GRANT
```
```text
bob=> \dp alice.t1
```
```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 alice  | t1   | table | alice=arwdDxt/alice+|                   |
        |      |       | bob=ar*w*dDxt/alice+|                   |
        |      |       | charlie=r*w/bob     |                   |
(1 row)
```
Роль может получить одну и ту же привилегию от разных ролей. Обратите внимание: если привилегию выдает суперпользователь, она выдается от имени владельца.
```sql
student=# GRANT UPDATE ON alice.t1 TO charlie;
```
```text
GRANT
```
```text
student=# \dp alice.t1
```

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

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

```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 alice  | t1   | table | alice=arwdDxt/alice+|                   |
        |      |       | bob=ar*w*dDxt/alice+|                   |
        |      |       | charlie=r*w/bob    +|                   |
        |      |       | charlie=w/alice     |                   |
(1 row)
```
Роль может отозвать привилегию или право передачи только у той роли, которой она их непосредственно выдала. Например, Алиса не сможет отозвать право передачи у Чарли, потому что она его не выдавала.
```sql
alice=> REVOKE GRANT OPTION FOR SELECT ON alice.t1 FROM charlie;
```
```text
REVOKE
```
Никакой ошибки не фиксируется, но и права не изменяются:
```text
alice=> \dp alice.t1
```
```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 alice  | t1   | table | alice=arwdDxt/alice+|                   |
        |      |       | bob=ar*w*dDxt/alice+|                   |
        |      |       | charlie=r*w/bob    +|                   |
        |      |       | charlie=w/alice     |                   |
(1 row)
```
В то же время Алиса не может просто так отозвать привилегии у Боба, если он успел передать их кому-либо еще:
```sql
alice=> REVOKE GRANT OPTION FOR SELECT ON alice.t1 FROM bob;
```
```text
ERROR:  dependent privileges exist
HINT:  Use CASCADE to revoke them too.
```
В таком случае надо отзывать и все переданные Бобом привилегии с помощью CASCADE:
```sql
alice=> REVOKE GRANT OPTION FOR SELECT ON alice.t1 FROM bob CASCADE;
```
```text
REVOKE
```
```text
alice=> \dp alice.t1
```
```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 alice  | t1   | table | alice=arwdDxt/alice+|                   |
        |      |       | bob=arw*dDxt/alice +|                   |
        |      |       | charlie=w/bob      +|                   |
        |      |       | charlie=w/alice     |                   |
(1 row)
```
Как видим, у Боба пропало право передачи привилегии, а у Чарли была отозвана и сама привилегия, поскольку она досталась Чарли только от Боба.
Аналогично можно отозвать по иерархии и привилегию:
```sql
alice=> REVOKE SELECT ON alice.t1 FROM bob CASCADE;
```
```text
REVOKE
```
```text
alice=> \dp alice.t1
```
```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 alice  | t1   | table | alice=arwdDxt/alice+|                   |
        |      |       | bob=aw*dDxt/alice  +|                   |
        |      |       | charlie=w/bob      +|                   |
        |      |       | charlie=w/alice     |                   |
(1 row)
```

## Page 20: Начальные привилегии {#source-page-020}

**Клонируемые объекты — привилегии в шаблонной БД**
- Новые объекты — привилегии по умолчанию
  - база данных
  - CONNECT, TEMPORARY
  - подпрограмма
  - EXECUTE
  - язык, домен, тип
  - USAGE

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

При создании объекта ролям назначаются определенные привилегии на него.

Поскольку базы данных клонируются из шаблонов, после создания базы объекты в ней уже имеют определенный набор привилегий. В частности, псевдороль PUBLIC, а вместе с ней и все роли, имеют привилегию USAGE на схемы public и information\_schema.

В PostgreSQL 14 и более ранних версиях псевдороль PUBLIC получала на схему public еще и привилегию CREATE; такая конфигурация небезопасна, поскольку позволяет подменить объект из схемы pg\_catalog собственным объектом. Подробности описаны в статье:

<https://wiki.postgresql.org/wiki/A\_Guide\_to\_CVE-2018-1058%3A\_Protect\_Y> our\_Search\_Path

Для новых объектов начальные привилегии определяются их типом. Для объектов некоторых типов псевдороль PUBLIC получает определенный набор привилегий — эти ситуации приведены на слайде.

Привилегии по умолчанию на новые объекты можно настроить для определенного владельца и схемы с помощью команды ALTER DEFAULT PRIVILEGES. Механизм срабатывает при создании объекта определенного класса в указанной (или любой) схеме определенным пользователем.

<https://postgrespro.ru/docs/postgresql/16/ddl-priv.html#PRIVILEGES-SUMM> ARY-TABLE

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

## Page 21: Начальные привилегии {#source-page-021}

Посмотрим на привилегии, выданные на схемы:
```text
student=# \dnS+
```
```text
                                                  List of schemas
        Name        |       Owner       |           Access privileges            |
   Description
--------------------+-------------------+----------------------------------------+--------
--------------------------
 alice              | alice             | alice=UC/alice                        +|
                    |                   | bob=U/alice                            |
 information_schema | postgres          | postgres=UC/postgres                  +|
                    |                   | =U/postgres                            |
 pg_catalog         | postgres          | postgres=UC/postgres                  +| system
catalog schema
                    |                   | =U/postgres                            |
 pg_toast           | postgres          |                                        |
reserved schema for TOAST tables
 public             | pg_database_owner | pg_database_owner=UC/pg_database_owner+|
standard public schema
                    |                   | =U/pg_database_owner                   |
(5 rows)
```
Слева от некоторых знаков равенства нет имени роли — это означает, что привилегии на схему public и две схемы системного каталога выданы псевдороли PUBLIC. Привилегии были выданы в шаблонной базе template1 и скопированы в базу access\_roles при ее создании.
Алиса создает простую функцию (она попадет в схему alice):
```sql
alice=> CREATE FUNCTION foo() RETURNS integer LANGUAGE sql RETURN 42;
```
```sql
CREATE FUNCTION
```
```sql
alice=> SELECT foo();
```
```text
 foo
-----
  42
(1 row)
```
Псевдороль PUBLIC автоматически получает привилегию EXECUTE для любой создаваемой функции. Поэтому, например, Боб может выполнить функцию, которую только что создала Алиса.
```sql
bob=> SELECT alice.foo();
```
```text
 foo
-----
  42
(1 row)
```
Если это нежелательно, можно отозвать привилегию EXECUTE у псевдороли PUBLIC и выдавать ее явно только нужным ролям:
```sql
alice=> REVOKE EXECUTE ON ALL ROUTINES
  IN SCHEMA alice
  FROM PUBLIC;
```
```text
REVOKE
```
```sql
bob=> SELECT alice.foo();
```
```text
ERROR:  permission denied for function foo
```
Однако на вновь создаваемые функции привилегия EXECUTE опять будет выдана всем ролям.
```sql
alice=> CREATE FUNCTION bar() RETURNS integer LANGUAGE sql RETURN 6174;
```
```sql
CREATE FUNCTION
```
И Боб сможет выполнить новую функцию Алисы:
```sql
bob=> SELECT alice.bar();
```

## Page 22: Page 22 {#source-page-022}

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

```text
 bar
------
 6174
(1 row)
```
При создании объекта на него выдается определенный набор начальных привилегий. Набор по умолчанию показывает функция acldefault:
```sql
alice=> SELECT acldefault(
  'f',             -- тип объекта (f — функция)
  'alice'::regrole -- OID владельца
);
```
```text
        acldefault
--------------------------
 {=X/alice,alice=X/alice}
(1 row)
```
Обратите внимание, что набор содержит привилегию EXECUTE для псевдороли PUBLIC.
Если Алиса не хочет автоматически разрешать всем ролям выполнять свои функции, она может исключить привилегию из начального набора:
```sql
alice=> ALTER DEFAULT PRIVILEGES
  REVOKE EXECUTE ON ROUTINES
  FROM PUBLIC;
```
```sql
ALTER DEFAULT PRIVILEGES
```
Можно и наоборот, выдавать дополнительные привилегии:
```sql
alice=> ALTER DEFAULT PRIVILEGES
  GRANT SELECT ON TABLES
  TO bob;
```
```sql
ALTER DEFAULT PRIVILEGES
```
Настроенные привилегии по умолчанию показывает метакоманда:
```text
alice=> \ddp
```
```text
            Default access privileges
 Owner | Schema |   Type   |  Access privileges
-------+--------+----------+---------------------
 alice |        | function | alice=X/alice
 alice |        | table    | alice=arwdDxt/alice+
       |        |          | bob=r/alice
(2 rows)
```
Теперь выполнение новых функций будет доступно только Алисе, а Боб будет получать доступ на чтение ко всем новым таблицам Алисы.
Проверим.
```sql
alice=> CREATE TABLE t3 AS SELECT * from t1;
```
```sql
SELECT 0
```
```text
alice=> \dp t3
```
```text
                             Access privileges
 Schema | Name | Type  |  Access privileges  | Column privileges | Policies
--------+------+-------+---------------------+-------------------+----------
 alice  | t3   | table | alice=arwdDxt/alice+|                   |
        |      |       | bob=r/alice         |                   |
(1 row)
```
```sql
bob=> SELECT * FROM alice.t3;
```
```text
 n
---
(0 rows)
```
```sql
alice=> CREATE FUNCTION baz() RETURNS integer LANGUAGE sql RETURN 1729;
```
```sql
CREATE FUNCTION
```
```sql
alice=> SELECT proname, proacl FROM pg_proc where proname='baz';
```

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

```text
 proname |     proacl
---------+-----------------
 baz     | {alice=X/alice}
(1 row)
```
```sql
bob=> SELECT * FROM alice.baz();
```
```text
ERROR:  permission denied for function baz
```

## Page 24: Итоги {#source-page-024}

**Роль — учетная запись СУБД и набор прав**
- Атрибуты определяют права роли, не привязанные к объектам
- Владелец объекта имеет неявные права на него
- Привилегии определяют права доступа ролей к объектам
- Привилегии можно передавать
- Начальные привилегии на объект определяются шаблоном базы данных или привилегиями по умолчанию

## Page 25: Практика {#source-page-025}

1. Создайте пользователей для Алисы и Боба.
- 2. Алиса создает две схемы: приватную alice и публичную app, и дает Бобу право обращаться к объектам публичной схемы.
- 3. Алиса создает таблицу с целочисленным столбцом в схеме app и дает Бобу привилегию на изменение ее строк. Сможет ли Боб увеличить значения во всех строках таблицы на единицу? А изменить значение всех строк на 42? Почему?
- 4. Алиса настраивает привилегии по умолчанию так, чтобы Боб получал право чтения строк любой таблицы, созданной Алисой в публичной схеме. Проверьте настройку.

## Page 26: 1. Роли {#source-page-026}

Зарегистрируем роли Алисы и Боба с правом подключения.
```sql
student=# CREATE USER alice;
```
```sql
CREATE ROLE
```
```sql
student=# CREATE USER bob;
```
```sql
CREATE ROLE
```
```sql
student=# CREATE DATABASE access_roles;
```
```sql
CREATE DATABASE
```
```sql
student=# GRANT CREATE ON DATABASE access_roles TO alice;
```
```text
GRANT
```
### 2. Схемы
```sh
student$ psql -U alice -d access_roles
```
Приватная схема:
```sql
alice=> CREATE SCHEMA alice;
```
```sql
CREATE SCHEMA
```
Публичная схема:
```sql
alice=> CREATE SCHEMA app;
```
```sql
CREATE SCHEMA
```
```text
alice=> GRANT USAGE ON SCHEMA app TO bob;
```
```text
GRANT
```
### 3. Таблица
```sql
alice=> CREATE TABLE app.t(n) AS VALUES (1),(2),(3);
```
```sql
SELECT 3
```
```sql
alice=> GRANT UPDATE ON app.t TO bob;
```
```text
GRANT
```
```sh
student$ psql -U bob -d access_roles
```
Так не работает:
```sql
bob=> UPDATE app.t SET n = n + 1;
```
```text
ERROR:  permission denied for table t
```
```sql
bob=> UPDATE app.t SET n = 42 WHERE n = 1;
```
```text
ERROR:  permission denied for table t
```
А так — работает:
```sql
bob=> UPDATE app.t SET n = 42;
```
```sql
UPDATE 3
```
Дело в том, что привилегия UPDATE дает право изменять значения столбцов, но не позволяет прочитать текущие значения. Поэтому привилегию UPDATE (как и DELETE) обычно выдают вместе с SELECT:
```sql
alice=> GRANT SELECT ON app.t TO bob;
```
```text
GRANT
```
```sql
bob=> UPDATE app.t SET n = n + 1;
```
```sql
UPDATE 3
```

## Page 27: 4. Привилегии по умолчанию {#source-page-027}

![Source slide page 27: 4. Привилегии по умолчанию](images/page-027.png){#source-figure-027 caption="Source slide page 27: 4. Привилегии по умолчанию"}

```sql
alice=> ALTER DEFAULT PRIVILEGES IN SCHEMA app
GRANT SELECT ON TABLES TO bob;
```
```sql
ALTER DEFAULT PRIVILEGES
```
Проверяем:
```sql
alice=> CREATE TABLE app.test(s text);
```
```sql
CREATE TABLE
```
```sql
bob=> SELECT * FROM app.test;
```
```text
 s
---
(0 rows)
```
### Уборка
```sql
bob=> \q
```
```sql
alice=> \q
```
```sql
student=# DROP DATABASE access_roles WITH (FORCE);
```
```sql
DROP DATABASE
```
```sql
student=# DROP ROLE alice, bob;
```
```sql
DROP ROLE
```

## Page 28: Практика+ {#source-page-028}

1. Создайте пользователя для Алисы и установите для него нулевое значение параметра log\_min\_duration\_statement.
- 2. Убедитесь, что все запросы Алисы записываются в журнал сообщений и что Алиса не может отменить эту запись.
- 3. Выдайте Алисе разрешение изменять параметр в своем сеансе. Убедитесь, что после этого она может отменить запись своих запросов в журнал сообщений.

## Page 29: 1. Пользователь и значение параметра {#source-page-029}

```sql
student=# CREATE USER alice;
```
```sql
CREATE ROLE
```
```sql
student=# ALTER ROLE alice SET log_min_duration_statement = 0;
```
```sql
ALTER ROLE
```
### 2. Сеанс Алисы
```sh
student$ psql -U alice -d student
```
```sql
alice=> SELECT now();
```
```text
             now
------------------------------
 2026-07-30 16:36:18.64657+03
(1 row)
```
Действия Алисы записываются в журнал сообщений:
```sh
student$ tail -n 1 /var/log/postgresql/postgresql-16-main.log
```
```sql
2026-07-30 16:36:18.647 MSK [60308] alice@student LOG:  duration: 0.311 ms  statement:
SELECT now();
```
И Алиса не может отменить эту запись:
```text
alice=> SET log_min_duration_statement = -1;
```
```text
ERROR:  permission denied to set parameter "log_min_duration_statement"
```
### 3. Привилегия на параметр
Дадим Алисе право менять значение параметра в текущем сеансе:
```text
student=# GRANT SET ON PARAMETER log_min_duration_statement TO alice;
```
```text
GRANT
```
Теперь она может избавиться от записи своих действий.
```text
alice=> SET log_min_duration_statement = -1;
```
```text
SET
```
```sql
alice=> SELECT 'I leave no traces now';
```
```text
       ?column?
-----------------------
 I leave no traces now
(1 row)
```
```sh
student$ tail -n 1 /var/log/postgresql/postgresql-16-main.log
```
```text
2026-07-30 16:36:18.864 MSK [60308] alice@student STATEMENT:  SET
log_min_duration_statement = -1;
```
### Уборка
```sql
alice=> \q
```
Удалить роль Алисы сейчас нельзя, от нее зависит привилегия на параметр:
```sql
student=# DROP ROLE alice;
```
```text
ERROR:  role "alice" cannot be dropped because some objects depend on it
DETAIL:  privileges for parameter log_min_duration_statement
```
Придется сначала удалить привилегию.
```sql
student=# REVOKE ALL ON PARAMETER log_min_duration_statement FROM alice;
```

## Page 30: Page 30 {#source-page-030}

```text
REVOKE
```
```sql
student=# DROP ROLE alice;
```
```sql
DROP ROLE
```
