diff --git a/doc/book/admin/access_control.rst b/doc/book/admin/access_control.rst index 87408b206b..0d64cef62a 100644 --- a/doc/book/admin/access_control.rst +++ b/doc/book/admin/access_control.rst @@ -1,377 +1,1072 @@ .. _authentication: +.. _access_control: -================================================================================ Access control -================================================================================ +============== -This section explains how Tarantool makes it possible for administrators -to prevent unauthorized access to the database and to certain functions. +Tarantool enables flexible management of access to various database resources. +The main concepts of Tarantool access control system are as follows: -Briefly: +* A *user* is a person or program that interacts with a Tarantool instance. +* An *object* is an entity to which access can be granted, for example, a space, an index, or a function. +* A *privilege* allows a user to perform certain operations on specific objects, for example, creating spaces, reading or updating data. +* A *role* is a named collection of privileges that can be granted to a user. -* There is a method to guarantee with password checks that users really are - who they say they are (“authentication”). -* There is a :ref:`_user ` system space, where usernames and - password-hashes are stored. -* There are functions for saying that certain users are allowed to do certain - things (“privileges”). +.. _access_control_overview: -* There is a :ref:`_priv ` system space, where privileges are - stored. Whenever a user tries to do an operation, there is a check whether - the user has the privilege to do the operation (“access control”). - -Details follow. +Overview +-------- .. _authentication-users: +.. _access_control_concepts_users: --------------------------------------------------------------------------------- Users --------------------------------------------------------------------------------- +~~~~~ -There is a **current user** for any program working with Tarantool, -local or remote. -If a remote connection is using a :ref:`binary port `, -the current user, by default, is '**guest**'. -If the connection is using an :ref:`admin-console port `, -the current user is '**admin**'. -When executing a :ref:`Lua initialization script `, -the current user is also ‘**admin**’. +A user identifies a person or program that interacts with a Tarantool instance. +There might be different types of users, for example: -The current user name can be found with -:doc:`/reference/reference_lua/box_session/user`. +* A database administrator responsible for the overall management and administration of a database. + An administrator can create other users and grant them specified privileges. +* A user with limited access to certain data and stored functions. + Such users can get their privileges from the database administrator. +* Users used in communications between Tarantool instances. For example, such users can be created to maintain replication and sharding in a Tarantool cluster. -The current user can be changed: +There are two built-in users in Tarantool: + +* ``admin`` is a user with all available administrative permissions. + If the connection uses an :ref:`admin-console port `, the current user is ``admin``. + For example, ``admin`` is used when connecting to an instance using :ref:`tt connect ` locally using the instance name: + + .. code-block:: console + + $ tt connect app:instance001 + + To allow remote :ref:`binary port ` connections using the ``admin`` user, you need to :ref:`set a password `. + +* ``guest`` is a user with minimum permissions used by default for remote :ref:`binary port ` connections. + For example, ``guest`` is used when connecting to an instance using :ref:`tt connect ` using the IP address and port without specifying the name of a user: + + .. code-block:: console + + $ tt connect 192.168.10.10:3301 + + .. WARNING:: + + Given that the ``guest`` user allows unauthenticated access to Tarantool instances, it is not recommended to grant additional privileges to this user. + For example, granting the ``execute`` access to :ref:`universe ` allows *remote code execution* on instances. + +.. NOTE:: + + Information about users is stored in the :ref:`_user ` space. -* For a binary port connection -- with the - :ref:`AUTH protocol command `, supported - by most clients; -* For an admin-console connection and in a Lua initialization script -- - with :doc:`/reference/reference_lua/box_session/su`; -* For a binary-port connection invoking a stored function with the CALL command -- - if the :doc:`SETUID ` - property is enabled for the function, - Tarantool temporarily replaces the current user with the - function’s creator, with all the creator's privileges, during function execution. .. _authentication-passwords: --------------------------------------------------------------------------------- Passwords --------------------------------------------------------------------------------- +~~~~~~~~~ -Each user (except 'guest') may have a **password**. -The password is any alphanumeric string. +Any user (except ``guest``) may have a password. +If a password is not set, a user cannot connect to Tarantool instances. -Tarantool passwords are stored in the :ref:`_user ` -system space with a -`cryptographic hash function `_ -so that, if the password is ‘x’, the stored hash-password is a long string -like ‘lL3OvhkIPOKh+Vn9Avlkx69M/Ck=‘. -Tarantool supports two protocols for authenticating users: +Tarantool :ref:`password hashes ` are stored in the :ref:`_user ` system space. +By default, Tarantool uses the ``CHAP`` protocol to authenticate users and applies ``SHA-1`` hashing to +:ref:`passwords `. +So, if the password is '123456', the stored hash is a string like 'a7SDfrdDKRBe5FaN2n3GftLKKtk='. +In the Enterprise Edition, you can enable ``PAP`` :ref:`authentication ` with the ``SHA256`` hashing algorithm. -* `CHAP `_ with ``SHA-1`` hashing +Tarantool Enterprise Edition allows you to improve database security by enforcing the use of strong passwords, setting up a maximum password age, and so on. +Learn more from the :ref:`configuration_authentication` topic. - In this case, password hashes are stored in the ``_user`` space `unsalted `_. - If an attacker gains access to the database, they may crack a password using, for example, a `rainbow table `_. -* `PAP `_ with ``SHA256`` hashing (Enterprise Edition) - For PAP, a password is salted with a user-unique salt before saving it in the ``_user`` space. - This keeps the database protected from cracking using a rainbow table. - Note that PAP sends a password as plain text, so you need to configure SSL/TLS for a connection. +.. _access_control_concepts_objects: -There are two functions for managing passwords in Tarantool: +Objects +~~~~~~~ -* :doc:`/reference/reference_lua/box_schema/user_passwd` allows you to change a user's password. +An object is a securable entity to which access can be granted. +Tarantool has a number of objects that enable flexible management of access to data, stored functions, specific actions, and so on. -* :doc:`/reference/reference_lua/box_schema/user_password` returns a hash of a user's password. +Below are a few examples of objects: -Tarantool Enterprise Edition also allows you to improve database security by enforcing the use of strong passwords, setting up a maximum password age, and so on. Learn more from the :ref:`configuration_authentication` topic. +* ``universe`` represents a database (:ref:`box.schema `) that contains database objects, including spaces, indexes, users, roles, sequences, and functions. + Granting privileges to ``universe`` gives a user access to any object in a database. +* ``space`` enables granting privileges to user-created or system :ref:`spaces `. +* ``function`` enables granting privileges to :ref:`functions `. +.. NOTE:: + + The full list of object types is available in the :ref:`access_control_list_objects` section. .. _authentication-owners_privileges: --------------------------------------------------------------------------------- -Owners and privileges --------------------------------------------------------------------------------- - -Tarantool has one database. It may be called "box.schema" or "universe". -The database contains database objects, including -spaces, indexes, users, roles, sequences, and functions. - -The **owner** of a database object is the user who created it. -The owner of the database itself, and the owner of objects that -are created initially (the system spaces and the default users) -is '**admin**'. - -Owners automatically have **privileges** for what they create. -They can share these privileges with other users or with roles, -using :doc:`/reference/reference_lua/box_schema/user_grant` requests. -The following privileges can be granted: - -* 'read', e.g. allow select from a space -* 'write', e.g. allow update on a space -* 'execute', e.g. allow call of a function, or (less commonly) allow use of a role -* 'create', e.g. allow - :doc:`box.schema.space.create ` - (access to certain system spaces is also necessary) -* 'alter', e.g. allow - :doc:`box.space.x.index.y:alter ` - (access to certain system spaces is also necessary) -* 'drop', e.g. allow - :doc:`box.sequence.x:drop ` - (access to certain system spaces is also necessary) -* 'usage', e.g. whether any action is allowable regardless of other - privileges (sometimes revoking 'usage' is a convenient way to - block a user temporarily without dropping the user) -* 'session', e.g. whether the user can 'connect'. - -To **create** objects, users need the 'create' privilege and -at least 'read' and 'write' privileges -on the system space with a similar name (for example, on the -:ref:`_space ` if the user needs to create spaces). - -To **access** objects, users need an appropriate privilege -on the object (for example, the 'execute' privilege on function F -if the users need to execute function F). See below some -:ref:`examples for granting specific privileges ` -that a grantor -- that is, 'admin' or the object creator -- can make. - -To drop an object, a user must be an 'admin' or have the 'super' role. -Some objects may also be dropped by their creators. -As the owner of the entire database, any 'admin' can drop any object, -including other users. - -To grant privileges to a user, the object owner says -:doc:`/reference/reference_lua/box_schema/user_grant`. -To revoke privileges from a user, the object owner says -:doc:`/reference/reference_lua/box_schema/user_revoke`. -In either case, there are up to five parameters: +Privileges +~~~~~~~~~~ -.. code-block:: lua +The privileges granted to a user determine which operations the user can perform, for example: - (user-name, privilege, object-type [, object-name [, options]]) +* The ``read`` and ``write`` privileges granted to the ``space`` :ref:`object ` allow a user to read or modify data in the specified space. +* The ``create`` privilege granted to the ``space`` object allows a user to create new spaces. +* The ``execute`` privilege granted to the ``function`` object allows a user to execute the specified function. +* The ``session`` privilege granted to a user allows connecting to an instance over IPROTO. -* ``user-name`` is the user (or role) that will receive or lose the privilege; -* ``privilege`` is any of 'read', 'write', 'execute', 'create', 'alter', 'drop', - 'usage', or 'session' (or a comma-separated list); -* ``object-type`` is any of 'space', 'index', - 'sequence', 'function', 'user', 'role', or 'universe'; -* ``object-name`` is what the privilege is for - (omitted if ``object-type`` is 'universe') - (may be omitted or ``nil`` if the intent is to grant for all objects of the same type); -* ``options`` is a list inside braces, for example ``{if_not_exists=true|false}`` - (usually omitted because the default is acceptable). +Note that some privileges might require read and write access to certain system spaces. +For example, the ``create`` privilege granted to the ``space`` object requires ``read`` and ``write`` privileges to the :ref:`_space ` system space. +Similarly, granting the ability to create functions requires ``read`` and ``write`` access to the :ref:`_func ` space. - All updates of user privileges are reflected immediately in the existing sessions - and objects, e.g. functions. +.. NOTE:: -**Example for granting many privileges at once** + Information about privileges is stored in the :ref:`_priv ` space. -In this example an 'admin' user grants many privileges on -many objects to user 'U', using a single request. -.. code-block:: lua - box.schema.user.grant('U','read,write,execute,create,drop','universe') +.. _access_control_concepts_roles: -.. _authentication-owners_privileges-examples-specific: +Roles +~~~~~ +A role is a container for :ref:`privileges ` that can be granted to users. +Roles can also be assigned to other roles, creating a role hierarchy. -**Examples for granting privileges for specific operations** +There are the following built-in roles in Tarantool: -In these examples an administrator grants strictly -the minimal privileges necessary for particular operations, -to user 'U'. +* ``super`` has all available administrative permissions. +* ``public`` has certain read permissions. This role is automatically granted to new users when they are created. +* ``replication`` can be granted to a user used to maintain :ref:`replication ` in a cluster. +* ``sharding`` can be granted to a user used to maintain :ref:`sharding ` in a cluster. -.. code-block:: lua + .. NOTE:: - -- So that 'U' can create spaces: - box.schema.user.grant('U','create','space') - box.schema.user.grant('U','write', 'space', '_schema') - box.schema.user.grant('U','write', 'space', '_space') - -- So that 'U' can create indexes on space T - box.schema.user.grant('U','create,read','space','T') - box.schema.user.grant('U','read,write','space','_space_sequence') - box.schema.user.grant('U','write', 'space', '_index') - -- So that 'U' can alter indexes on space T (assuming 'U' did not create the index) - box.schema.user.grant('U','alter','space','T') - box.schema.user.grant('U','read','space','_space') - box.schema.user.grant('U','read','space','_index') - box.schema.user.grant('U','read','space','_space_sequence') - box.schema.user.grant('U','write','space','_index') - -- So that 'U' can alter indexes on space T (assuming 'U' created the index) - box.schema.user.grant('U','read','space','_space_sequence') - box.schema.user.grant('U','read,write','space','_index') - -- So that 'U' can create users: - box.schema.user.grant('U','create','user') - box.schema.user.grant('U', 'read,write', 'space', '_user') - box.schema.user.grant('U', 'write', 'space', '_priv') - -- So that 'U' can create roles: - box.schema.user.grant('U','create','role') - box.schema.user.grant('U', 'read,write', 'space', '_user') - box.schema.user.grant('U', 'write', 'space', '_priv') - -- So that 'U' can create sequence generators: - box.schema.user.grant('U','create','sequence') - box.schema.user.grant('U', 'read,write', 'space', '_sequence') - -- So that 'U' can create functions: - box.schema.user.grant('U','create','function') - box.schema.user.grant('U','read,write','space','_func') - -- So that 'U' can create any object of any type - box.schema.user.grant('U','read,write,create','universe') - -- So that 'U' can grant access on objects that 'U' created - box.schema.user.grant('U','write','space','_priv') - -- So that 'U' can select or get from a space named 'T' - box.schema.user.grant('U','read','space','T') - -- So that 'U' can update or insert or delete or truncate a space named 'T' - box.schema.user.grant('U','write','space','T') - -- So that 'U' can execute a function named 'F' - box.schema.user.grant('U','execute','function','F') - -- So that 'U' can use the "S:next()" function with a sequence named S - box.schema.user.grant('U','read,write','sequence','S') - -- So that 'U' can use the "S:set()" or "S:reset() function with a sequence named S - box.schema.user.grant('U','write','sequence','S') - -- So that 'U' can drop a sequence (assuming 'U' did not create it) - box.schema.user.grant('U','drop','sequence') - box.schema.user.grant('U','write','space','_sequence_data') - box.schema.user.grant('U','write','space','_sequence') - -- So that 'U' can drop a function (assuming 'U' did not create it) - box.schema.user.grant('U','drop','function') - box.schema.user.grant('U','write','space','_func') - -- So that 'U' can drop a space that has some associated objects - box.schema.user.grant('U','create,drop','space') - box.schema.user.grant('U','write','space','_schema') - box.schema.user.grant('U','write','space','_space') - box.schema.user.grant('U','write','space','_space_sequence') - box.schema.user.grant('U','read','space','_trigger') - box.schema.user.grant('U','read','space','_fk_constraint') - box.schema.user.grant('U','read','space','_ck_constraint') - box.schema.user.grant('U','read','space','_func_index') - -- So that 'U' can drop any space (ignore if the privilege exists already) - box.schema.user.grant('U','drop','space',nil,{if_not_exists=true}) - -**Example for creating users and objects then granting privileges** - -Here a Lua function is created that will be executed under the user ID of its -creator, even if called by another user. + The ``sharding`` role is created only if an instance is managed using :ref:`YAML configuration `. -First, the two spaces ('u' and 'i') are created, and a no-password user ('internal') -is granted full access to them. Then a ('read_and_modify') is defined and the -no-password user becomes this function's creator. Finally, another user -('public_user') is granted access to execute Lua functions created by the no-password user. +Below are a few diagrams that demonstrate how privileges can be granted to a user without and with using roles. -.. code-block:: lua +* In this example, a user gets privileges directly without using roles. - box.schema.space.create('u') - box.schema.space.create('i') - box.space.u:create_index('pk') - box.space.i:create_index('pk') + .. code-block:: none - box.schema.user.create('internal') + user1 ── privilege1 + ├─── privilege2 + └─── privilege3 - box.schema.user.grant('internal', 'read,write', 'space', 'u') - box.schema.user.grant('internal', 'read,write', 'space', 'i') - box.schema.user.grant('internal', 'create', 'universe') - box.schema.user.grant('internal', 'read,write', 'space', '_func') +* In this example, a user gets all privileges provided by ``role1`` and specific privileges assigned directly. - function read_and_modify(key) - local u = box.space.u - local i = box.space.i - local fiber = require('fiber') - local t = u:get{key} - if t ~= nil then - u:put{key, box.session.uid()} - i:put{key, fiber.time()} - end - end + .. code-block:: none - box.session.su('internal') - box.schema.func.create('read_and_modify', {setuid= true}) - box.session.su('admin') - box.schema.user.create('public_user', {password = 'secret'}) - box.schema.user.grant('public_user', 'execute', 'function', 'read_and_modify') + user1 ── role1 ── privilege1 + │ └─── privilege2 + ├─── privilege3 + └─── privilege4 -.. _authentication-roles: +* In this example, ``role2`` is granted to ``role1``. + This means that a user with ``role1`` subsequently gets all privileges from both roles ``role1`` and ``role2``. --------------------------------------------------------------------------------- -Roles --------------------------------------------------------------------------------- + .. code-block:: none -A **role** is a container for privileges which can be granted to regular users. -Instead of granting or revoking individual privileges, you can put all the -privileges in a role and then grant or revoke the role. + user1 ── role1 ── privilege1 + │ ├─── privilege2 + │ └─── role2 + │ ├─── privilege3 + │ └─── privilege4 + ├─── privilege5 + └─── privilege6 -Role information is stored in the :ref:`_user ` space, but -the third field in the tuple -- the type field -- is ‘role’ rather than ‘user’. +.. NOTE:: -An important feature in role management is that roles can be **nested**. -For example, role R1 can be granted a privileged "role R2", so users with the -role R1 will subsequently get all privileges from both roles R1 and R2. -In other words, a user gets all the privileges granted to a user’s roles, -directly or indirectly. + Information about roles is stored in the :ref:`_user ` space. -There are actually two ways to grant or revoke a role: -:samp:`box.schema.user.grant-or-revoke({user-name-or-role-name},'execute', 'role',{role-name}...)` -or -:samp:`box.schema.user.grant-or-revoke({user-name-or-role-name},{role-name}...)`. -The second way is preferable. -The 'usage' and 'session' privileges cannot be granted to roles. -**Example** +.. _authentication-owners: + +Object owners +~~~~~~~~~~~~~ + +An owner of a database :ref:`object ` is the user who created it. +The owner of the database and the owner of objects that are created initially (the system spaces and the default users) is the ``admin`` :ref:`user `. + +Owners automatically have :ref:`privileges ` for objects they create. +They can :ref:`share these privileges ` with other users or roles using ``box.schema.user.grant()`` and ``box.schema.role.grant()``. + +.. NOTE:: + + Information about users who gave the specified privileges is stored in the :ref:`_priv ` space. -.. code-block:: lua - -- This example will work for a user with many privileges, such as 'admin' - -- or a user with the pre-defined 'super' role - -- Create space T with a primary index - box.schema.space.create('T') - box.space.T:create_index('primary', {}) - -- Create the user U1 so that later the current user can be changed to U1 - box.schema.user.create('U1') - -- Create two roles, R1 and R2 - box.schema.role.create('R1') - box.schema.role.create('R2') - -- Grant role R2 to role R1 and role R1 to user U1 (order doesn't matter) - -- There are two ways to grant a role; here the shorter way is used - box.schema.role.grant('R1', 'R2') - box.schema.user.grant('U1', 'R1') - -- Grant read/write privileges for space T to role R2 - -- (but not to role R1, and not to user U1) - box.schema.role.grant('R2', 'read,write', 'space', 'T') - -- Change the current user to user U1 - box.session.su('U1') - -- An insertion to space T will now succeed because (due to nested roles) - -- user U1 has write privilege on space T - box.space.T:insert{1} - -More details are to be found in -:doc:`/reference/reference_lua/box_schema/user_grant` and -:doc:`/reference/reference_lua/box_schema/role_grant` in -the built-in modules reference. .. _authentication-sessions: --------------------------------------------------------------------------------- -Sessions and security --------------------------------------------------------------------------------- +Sessions +~~~~~~~~ -A **session** is the state of a connection to Tarantool. It contains: +A session is the state of a connection to Tarantool. +The session contains: -* An integer ID identifying the connection, -* the :ref:`current user ` associated with the connection, -* text description of the connected peer, and -* session local state, such as Lua variables and functions. +* An integer ID identifying the connection. +* The current :ref:`user ` associated with the connection. +* The text description of the connected peer. +* A session's local state, such as Lua variables and functions. In Tarantool, a single session can execute multiple concurrent transactions. Each transaction is identified by a unique integer ID, which can be queried -at start of the transaction using :doc:`/reference/reference_lua/box_session/sync`. +at the start of the transaction using :doc:`/reference/reference_lua/box_session/sync`. + +.. NOTE:: + + To track all connects and disconnects, you can use :ref:`connection and authentication triggers `. + + + + +.. _access_control_users: + +Managing users +-------------- + +.. _access_control_user_creating: + +Creating a user +~~~~~~~~~~~~~~~ + +To create a new user, call :ref:`box.schema.user.create() `. +In the example below, a user is created without a password: + +.. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Create a user without a password + :end-before: End: Create a user without a password + :dedent: + + +In this example, the password is specified in the ``options`` parameter: + +.. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Create a user with a password + :end-before: End: Create a user with a password + :dedent: + + +.. _access_control_user_changing_passwords: + +Changing passwords +~~~~~~~~~~~~~~~~~~ + +To set or change a user's password, use :ref:`box.schema.user.passwd() `. +In the example below, a user password is set for a currently logged-in user: + +.. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Set a password for the current user + :end-before: End: Set a password for the current user + :dedent: + +To set the password for the specified user, pass a username and password as shown below: + +.. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Set a password for the specified user + :end-before: End: Set a password for the specified user + :dedent: + +.. NOTE:: + + :doc:`/reference/reference_lua/box_schema/user_password` returns a hash of the specified password. + + + +.. _access_control_user_granting_privileges: + +Granting privileges to a user +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To grant the specified privileges to a user, use the :ref:`box.schema.user.grant() ` function. +In the example below, ``testuser`` gets read privileges to the ``writers`` space and read/write privileges to the ``books`` space: + +.. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Grant privileges to the specified user + :end-before: End: Grant privileges to the specified user + :dedent: + +Learn more about granting privileges to different types of objects from :ref:`access_control_granting_privileges`. + + + +.. _access_control_user_info: + +Getting a user's information +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To check whether the specified user exists, call :ref:`box.schema.user.exists() `: + +.. code-block:: lua + + box.schema.user.exists('testuser') + --[[ + - true + --]] + +To get information about privileges granted to a user, call :ref:`box.schema.user.info() `: + +.. code-block:: lua + + box.schema.user.info('testuser') + --[[ + - - - execute + - role + - public + - - read + - space + - writers + - - read,write + - space + - books + - - session,usage + - universe + - + - - alter + - user + - testuser + --]] + +In the example above, ``testuser`` has the following privileges: + +* The ``execute`` privilege to the ``public`` role means that this role is :ref:`assigned to the user `. + +* The ``read`` privilege to the ``writers`` space means that the user can read data from this space. + +* The ``read,write`` privileges to the ``books`` space mean that the user can read and modify data in this space. + +* The ``session,usage`` privileges to ``universe`` mean the following: + + * ``session``: the user can authenticate over an IPROTO connection. + * ``usage``: lets the user use their privileges on database objects (for example, read and modify data in a space). + +* The ``alter`` privilege lets ``testuser`` modify its own settings, for example, a password. + + + +.. _access_control_user_revoking_privileges: + +Revoking user's privileges +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To revoke the specified privileges, use the :ref:`box.schema.user.revoke() ` function. +In the example below, write access to the ``books`` space is revoked: + +.. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Revoke space reading + :end-before: End: Revoke space reading + :dedent: + +Revoking the ``session`` privilege from ``universe`` can be used to disallow a user to connect to a Tarantool instance: + +.. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Revoke session + :end-before: End: Revoke session + :dedent: + + +.. _access_control_users_current_user: + +Changing the current user +~~~~~~~~~~~~~~~~~~~~~~~~~ + +The current user name can be found using :ref:`box.session.user() `. + +.. code-block:: lua + + box.session.user() + --[[ + - admin + --]] + +The current user can be changed: + +* For an :ref:`admin-console ` connection: using :doc:`/reference/reference_lua/box_session/su`: + + .. code-block:: lua + + box.session.su('testuser') + box.session.user() + --[[ + - testuser + --]] + +* For a binary port connection: using the + :ref:`AUTH protocol command `, supported by most clients. + +* For a binary-port connection invoking a stored function with the CALL command: + if the :doc:`SETUID ` + property is enabled for the function, + Tarantool temporarily replaces the current user with the + function's creator, with all the creator's privileges, during function execution. + + + +.. _access_control_users_dropping: + +Dropping users +~~~~~~~~~~~~~~ + +To drop the specified user, call :ref:`box.schema.user.drop() `: + +.. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Drop a user + :end-before: End: Drop a user + :dedent: + + + + +.. _authentication-roles: +.. _access_control_roles: + +Managing roles +-------------- + +.. _access_control_roles_creating: + +Creating a role +~~~~~~~~~~~~~~~ + +To create a new role, call :ref:`box.schema.role.create() `. +In the example below, two roles are created: + +.. literalinclude:: /code_snippets/test/access_control/grant_roles_test.lua + :language: lua + :start-after: Create roles + :end-before: End: Create roles + :dedent: + + +.. _access_control_roles_granting_privileges: + +Granting privileges to a role +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To grant the specified privileges to a role, use the :ref:`box.schema.role.grant() ` function. +In the example below, the ``books_space_manager`` role gets read and write privileges to the ``books`` space: + +.. literalinclude:: /code_snippets/test/access_control/grant_roles_test.lua + :language: lua + :start-after: Grant read/write privileges to a role + :end-before: Grant write privileges to a role + :dedent: + +The ``writers_space_reader`` role gets read privileges to the ``writers`` space: + +.. literalinclude:: /code_snippets/test/access_control/grant_roles_test.lua + :language: lua + :start-after: Grant write privileges to a role + :end-before: End: Grant privileges to roles + :dedent: + +Learn more about granting privileges to different types of objects from :ref:`access_control_granting_privileges`. + +.. NOTE:: + + Not all privileges can be granted to roles. + Learn more from :ref:`access_control_list_privileges`. + + +.. _access_control_roles_granting_role: + +Granting a role to a role +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Roles can be assigned to other roles. +In the example below, the newly created ``all_spaces_manager`` role gets all privileges granted to ``books_space_manager`` and ``writers_space_reader``: + +.. literalinclude:: /code_snippets/test/access_control/grant_roles_test.lua + :language: lua + :start-after: Grant a role to a role + :end-before: End: Grant a role to a role + :dedent: + + +.. _access_control_roles_granting_user: + +Granting a role to a user +~~~~~~~~~~~~~~~~~~~~~~~~~ + +To grant the specified role to a :ref:`user `, use the ``box.schema.user.grant()`` function. +In the example below, ``testuser`` gets privileges granted to the ``books_space_manager`` and ``writers_space_reader`` roles: + +.. literalinclude:: /code_snippets/test/access_control/grant_roles_test.lua + :language: lua + :start-after: Grant a role to a user + :end-before: End: Grant a role to a user + :dedent: + + +.. _access_control_roles_info: + +Getting a role's information +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To check whether the specified role exists, call :ref:`box.schema.role.exists() `: + +.. code-block:: lua + + box.schema.role.exists('books_space_manager') + --[[ + - true + --]] + +To get information about privileges granted to a role, call :ref:`box.schema.role.info() `: + +.. code-block:: lua + + box.schema.role.info('books_space_manager') + --[[ + - - - read,write + - space + - books + --]] + +If a role has the ``execute`` privilege to other roles, this means that these roles are :ref:`granted to this parent role `: + +.. code-block:: lua + + box.schema.role.info('all_spaces_manager') + --[[ + - - - execute + - role + - books_space_manager + - - execute + - role + - writers_space_reader + --]] + + -.. NOTE:: - To track all connects and disconnects, you can use - :ref:`connection and authentication triggers `. +.. _access_control_roles_revoking_role: + +Revoking a role from a user +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To revoke the specified role from a user, revoke the ``execute`` privilege for this role using the :ref:`box.schema.user.revoke() ` function. +In the example below, the ``books_space_reader`` role is revoked from ``testuser``: + +.. literalinclude:: /code_snippets/test/access_control/grant_roles_test.lua + :language: lua + :start-after: Revoking a role from a user + :end-before: End: Revoking a role from a user + :dedent: + +To revoke role's privileges, use :ref:`box.schema.role.revoke() `. + + +.. _access_control_roles_dropping: + +Dropping roles +~~~~~~~~~~~~~~ + +To drop the specified role, call :ref:`box.schema.role.drop() `: + +.. literalinclude:: /code_snippets/test/access_control/grant_roles_test.lua + :language: lua + :start-after: Dropping a role + :end-before: End: Dropping a role + :dedent: + + + +.. _access_control_granting_privileges: + +Granting privileges +------------------- + +To grant the specified privileges to a user or role, use the :ref:`box.schema.user.grant() ` and :ref:`box.schema.role.grant() ` functions, +which have similar signatures and accept the same set of arguments. +For example, the ``box.schema.user.grant()`` signature looks as follows: + +.. code-block:: lua + + box.schema.user.grant(username, privileges, object-type, object-name[, {options}]) + +* ``username``: the name of the user that gets the specified privileges. +* ``privileges``: a string value that represents :ref:`privileges ` granted to the user. If there are several privileges, they should be separated by commas without a space. +* ``object-type``: a type of :ref:`object ` to which privileges are granted. +* ``object-name``: the name of the object to which privileges are granted. + An empty string (``""``) or ``nil`` provided instead of ``object-name`` grants the specified privileges to all objects of the specified type. + + .. NOTE:: + + ``object-name`` is ignored for the following combinations of privileges and object types: + + * Any privilege granted to ``universe``. + * The ``create`` and ``drop`` privileges for the following object types: ``user``, ``role``, ``space``, ``function``, ``sequence``. + * The ``execute`` privilege for the following object types: ``lua_eval``, ``lua_call``, ``sql``. + + +.. _access_control_grant_creating_any_obj: + +Any object +~~~~~~~~~~ + +In the example below, ``testuser`` gets privileges allowing them to create any object of any type: + +.. code-block:: lua + + box.schema.user.grant('testuser','read,write,create','universe') + +In this example, ``testuser`` can grant access to objects that ``testuser`` created: + +.. code-block:: lua + + box.schema.user.grant('testuser','write','space','_priv') + + + + +.. _access_control_grant_spaces: + +Spaces +~~~~~~ + +.. _access_control_grant_spaces_create: + +Creating and altering spaces +**************************** + +In the example below, ``testuser`` gets privileges allowing them to create :ref:`spaces `: + +.. code-block:: lua + + box.schema.user.grant('testuser','create','space') + box.schema.user.grant('testuser','write', 'space', '_schema') + box.schema.user.grant('testuser','write', 'space', '_space') + +As you can see, the ability to create spaces also requires ``write`` access to certain system spaces. + +To allow ``testuser`` to drop a space that has associated objects, add the following privileges: + +.. code-block:: lua + + box.schema.user.grant('testuser','create,drop','space') + box.schema.user.grant('testuser','write','space','_schema') + box.schema.user.grant('testuser','write','space','_space') + box.schema.user.grant('testuser','write','space','_space_sequence') + box.schema.user.grant('testuser','read','space','_trigger') + box.schema.user.grant('testuser','read','space','_fk_constraint') + box.schema.user.grant('testuser','read','space','_ck_constraint') + box.schema.user.grant('testuser','read','space','_func_index') + + +.. _access_control_grant_spaces_indexes: + +Creating and altering indexes +***************************** + +In the example below, ``testuser`` gets privileges allowing them to create :ref:`indexes ` in the 'writers' space: + +.. code-block:: lua + + box.schema.user.grant('testuser','create,read','space','writers') + box.schema.user.grant('testuser','read,write','space','_space_sequence') + box.schema.user.grant('testuser','write', 'space', '_index') + +To allow ``testuser`` to alter indexes in the 'writers' space, grant the privileges below. +This example assumes that indexes in the 'writers' space are not created by ``testuser``. + +.. code-block:: lua + + box.schema.user.grant('testuser','alter','space','writers') + box.schema.user.grant('testuser','read','space','_space') + box.schema.user.grant('testuser','read','space','_index') + box.schema.user.grant('testuser','read','space','_space_sequence') + box.schema.user.grant('testuser','write','space','_index') + +If ``testuser`` created indexes in the 'writers' space, granting the following privileges is enough to alter indexes: + +.. code-block:: lua + + box.schema.user.grant('testuser','read','space','_space_sequence') + box.schema.user.grant('testuser','read,write','space','_index') + + +.. _access_control_grant_spaces_crud: + +CRUD operations +*************** + +In this example, ``testuser`` gets privileges allowing them to :ref:`select data ` from the 'writers' space: + +.. code-block:: lua + + box.schema.user.grant('testuser','read','space','writers') + +In this example, ``testuser`` is allowed to read and modify data in the 'books' space: + +.. code-block:: lua + + box.schema.user.grant('testuser','read,write','space','books') + + + + +.. _access_control_grant_sequences: + +Sequences +~~~~~~~~~ + +.. _access_control_grant_sequences_create_drop: + +Creating and dropping sequences +******************************* + +In this example, ``testuser`` gets privileges to create :ref:`sequence ` generators: + +.. code-block:: lua + + box.schema.user.grant('testuser','create','sequence') + box.schema.user.grant('testuser', 'read,write', 'space', '_sequence') + +To let ``testuser`` drop a sequence, grant them the following privileges: + +.. code-block:: lua + + box.schema.user.grant('testuser','drop','sequence') + box.schema.user.grant('testuser','write','space','_sequence_data') + box.schema.user.grant('testuser','write','space','_sequence') + +.. _access_control_grant_sequences_functions: + +Using sequence functions +************************ + +In this example, ``testuser`` is allowed to use the ``id_seq:next()`` function with a sequence named 'id_seq': + +.. code-block:: lua + + box.schema.user.grant('testuser','read,write','sequence','id_seq') + +In the next example, ``testuser`` is allowed to use the ``id_seq:set()`` or ``id_seq:reset()`` functions with a sequence named 'id_seq': + +.. code-block:: lua + + box.schema.user.grant('testuser','write','sequence','id_seq') + + + +.. _access_control_grant_functions: + +Functions +~~~~~~~~~ + +.. _access_control_grant_functions_create_drop: + +Creating and dropping functions +******************************* + +In this example, ``testuser`` gets privileges to create :ref:`functions `: + +.. code-block:: lua + + box.schema.user.grant('testuser','create','function') + box.schema.user.grant('testuser','read,write','space','_func') + +To let ``testuser`` drop a function, grant them the following privileges: + +.. code-block:: lua + + box.schema.user.grant('testuser','drop','function') + box.schema.user.grant('testuser','write','space','_func') + + +.. _access_control_grant_functions_execute: + +Executing functions +******************* + +To give the ability to execute a function named 'sum', grant the following privileges: + +.. code-block:: lua + + box.schema.user.grant('testuser','execute','function','sum') + + + + + +.. _access_control_grant_users: + +Users +~~~~~ + +In this example, ``testuser`` gets privileges to create other users: + +.. code-block:: lua + + box.schema.user.grant('testuser','create','user') + box.schema.user.grant('testuser', 'read,write', 'space', '_user') + box.schema.user.grant('testuser', 'write', 'space', '_priv') + +.. _access_control_grant_roles: + +Roles +~~~~~ + +To let ``testuser`` create new roles, grant the following privileges: + +.. code-block:: lua + + box.schema.user.grant('testuser','create','role') + box.schema.user.grant('testuser', 'read,write', 'space', '_user') + box.schema.user.grant('testuser', 'write', 'space', '_priv') + + +.. _access_control_grant_execute_code: + +Executing code +~~~~~~~~~~~~~~ + +To let ``testuser`` execute Lua code, grant the ``execute`` privilege to the ``lua_eval`` object: + +.. code-block:: lua + + box.schema.user.grant('testuser','execute','lua_eval') + +Similarly, executing an arbitrary SQL expression requires the ``execute`` privilege to the ``sql`` object: + +.. code-block:: lua + + box.schema.user.grant('testuser','execute','sql') + + + + + +.. _creating_users_and_objects_granting_privileges: + +Example +~~~~~~~ + +In the example below, the :ref:`created Lua function ` is executed on behalf of its +creator, even if called by another user. + +First, the two spaces (``space1`` and ``space2``) are created, and a no-password user (``private_user``) +is granted full access to them. Then ``read_and_modify`` is defined and ``private_user`` becomes this function's creator. +Finally, another user (``public_user``) is granted access to execute Lua functions created by ``private_user``. + +.. code-block:: lua + + box.schema.space.create('space1') + box.schema.space.create('space2') + box.space.space1:create_index('pk') + box.space.space2:create_index('pk') + + box.schema.user.create('private_user') + + box.schema.user.grant('private_user', 'read,write', 'space', 'space1') + box.schema.user.grant('private_user', 'read,write', 'space', 'space2') + box.schema.user.grant('private_user', 'create', 'universe') + box.schema.user.grant('private_user', 'read,write', 'space', '_func') + + function read_and_modify(key) + local space1 = box.space.space1 + local space2 = box.space.space2 + local fiber = require('fiber') + local t = space1:get{key} + if t ~= nil then + space1:put{key, box.session.uid()} + space2:put{key, fiber.time()} + end + end + + box.session.su('private_user') + box.schema.func.create('read_and_modify', {setuid= true}) + box.session.su('admin') + box.schema.user.create('public_user', {password = 'secret'}) + box.schema.user.grant('public_user', 'execute', 'function', 'read_and_modify') + +Whenever ``public_user`` calls the function, it is executed on behalf of its creator, ``private_user``. + + + + +.. _access_control_list: + +All object types and privileges +------------------------------- + +.. _access_control_list_objects: + +Object types +~~~~~~~~~~~~ + +.. container:: table + + .. list-table:: + :header-rows: 1 + :widths: 20 80 + + * - Object type + - Description + * - ``universe`` + - A database (:ref:`box.schema `) that contains database objects, including spaces, indexes, users, roles, sequences, and functions. Granting privileges to ``universe`` gives a user access to any object in the database. + * - ``user`` + - A :ref:`user `. + * - ``role`` + - A :ref:`role `. + * - ``space`` + - A :ref:`space `. + * - ``function`` + - A :ref:`function `. + * - ``sequence`` + - A :ref:`sequence `. + * - ``lua_eval`` + - Executing arbitrary Lua code. + * - ``lua_call`` + - Calling any global user-defined Lua function. + * - ``sql`` + - Executing an arbitrary SQL expression. + + + +.. _access_control_list_privileges: + +Privileges +~~~~~~~~~~ + +.. container:: table + + .. list-table:: + :header-rows: 1 + :widths: 15 15 15 55 + + * - Privilege + - Object type + - Granted to roles + - Description + * - ``read`` + - All + - Yes + - Allows reading data of the specified object. + For example, this privilege can be used to allow a user to select data from the specified space. + * - ``write`` + - All + - Yes + - Allows updating data of the specified object. + For example, this privilege can be used to allow a user to modify data in the specified space. + * - ``create`` + - All + - Yes + - Allows creating objects of the specified type. + For example, this privilege can be used to allow a user to create new spaces. + + Note that this privilege requires read and write access to certain system spaces. + * - ``alter`` + - All + - Yes + - Allows altering objects of the specified type. + + Note that this privilege requires read and write access to certain system spaces. + * - ``drop`` + - All + - Yes + - Allows dropping objects of the specified type. + + Note that this privilege requires read and write access to certain system spaces. + * - ``execute`` + - ``role``, ``universe``, ``function``, ``lua_eval``, ``lua_call``, ``sql`` + - Yes + - For ``role``, allows using the specified role. + For other object types, allows calling a function. + * - ``session`` + - ``universe`` + - No + - Allows a user to connect to an instance over IPROTO. + * - ``usage`` + - ``universe`` + - No + - Allows a user to use their privileges on database objects (for example, read, write, and alter spaces). + + +.. _access_control_list_objects_and_privileges: + +Object types and privileges +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. container:: table + + .. list-table:: + :header-rows: 1 + :widths: 15 85 + + * - Object type + - Details + * - ``universe`` + - * ``read``: Allows reading any object types, including all spaces or sequence objects. + * ``write``: Allows modifying any object types, including all spaces or sequence objects. + * ``execute``: Allows execute functions, Lua code, or SQL expressions, including IPROTO calls. + * ``session``: Allows a user to connect to an instance over IPROTO. + * ``usage``: Allows a user to use their privileges on database objects (for example, read, write, and alter space). + * ``create``: Allows creating users, roles, functions, spaces, and sequences. + This privilege requires read and write access to certain system spaces. + * ``drop``: Allows creating users, roles, functions, spaces, and sequences. + This privilege requires read and write access to certain system spaces. + * ``alter``: Allows altering user settings or space objects. + * - ``user`` + - * ``alter``: Allows modifying a user description, for example, change the password. + * ``create``: Allows creating new users. + This privilege requires read and write access to the ``_user`` system space. + * ``drop``: Allows dropping users. + This privilege requires read and write access to the ``_user`` system space. + * - ``role`` + - * ``execute``: Indicates that a role is assigned to the user or another role. + * ``create``: Allows creating new roles. + This privilege requires read and write access to the ``_user`` system space. + * ``drop``: Allows dropping roles. + This privilege requires read and write access to the ``_user`` system space. + * - ``space`` + - * ``read``: Allows selecting data from a space. + * ``write``: Allows modifying data in a space. + * ``create``: Allows creating new spaces. + This privilege requires read and write access to the ``_space`` system space. + * ``drop``: Allows dropping spaces. + This privilege requires read and write access to the ``_space`` system space. + * ``alter``: Allows modifying spaces. + This privilege requires read and write access to the ``_space`` system space. + + If a space is created by a user, they can read and write it without granting explicit privileges. + * - ``function`` + - * ``execute``: Allows calling a function. + * ``create``: Allows creating a function. + This privilege requires read and write access to the ``_func`` system space. + + If a function is created by a user, they can execute it without granting explicit privileges. + * ``drop``: Allows dropping a function. + This privilege requires read and write access to the ``_func`` system space. + * - ``sequence`` + - * ``read``: Allows using sequences in ``space_obj:create_index()``. + * ``write``: Allows all operations for a sequence object. + + ``seq_obj:drop()`` requires a write privilege to the ``_priv`` system space. + * ``create``: Allows creating sequences. + This privilege requires read and write access to the ``_sequence`` system space. + + If a sequence is created by a user, they can read/write it without explicit privilege. + * ``drop``: Allows dropping sequences. + This privilege requires read and write access to the ``_sequence`` system space. + * ``alter``: Has no effect. + ``seq_obj:alter()`` and other methods require the ``write`` privilege. + * - ``lua_eval`` + - * ``execute``: Allows executing arbitrary Lua code using the IPROTO_EVAL request. + * - ``lua_call`` + - * ``execute``: Allows executing any user-defined function using the IPROTO_CALL request. + This privilege doesn't allow a user to call built-in Lua functions (for example, ``loadstring()`` or ``box.session.su()``) and functions defined in the ``_func`` system space. + * - ``sql`` + - * ``execute``: Allows executing arbitrary SQL expression using the IPROTO_PREPARE and IPROTO_EXECUTE requests. diff --git a/doc/code_snippets/test/access_control/grant_roles_test.lua b/doc/code_snippets/test/access_control/grant_roles_test.lua new file mode 100644 index 0000000000..55204594fa --- /dev/null +++ b/doc/code_snippets/test/access_control/grant_roles_test.lua @@ -0,0 +1,104 @@ +local fio = require('fio') +local server = require('luatest.server') +local t = require('luatest') +local g = t.group() +g.before_all(function(cg) + cg.server = server:new { + box_cfg = {}, + workdir = fio.cwd() .. '/tmp' + } + cg.server:start() + cg.server:exec(function() + box.schema.space.create('writers') + box.space.writers:format({ + { name = 'id', type = 'unsigned' }, + { name = 'name', type = 'string' } + }) + box.space.writers:create_index('primary', { parts = { 'id' } }) + + box.schema.space.create('books') + box.space.books:format({ + { name = 'id', type = 'unsigned' }, + { name = 'title', type = 'string' }, + { name = 'author_id', foreign_key = { space = 'writers', field = 'id' } }, + }) + box.space.books:create_index('primary', { parts = { 'id' } }) + + box.space.writers:insert { 1, 'Leo Tolstoy' } + box.space.writers:insert { 2, 'Fyodor Dostoevsky' } + box.space.writers:insert { 3, 'Alexander Pushkin' } + + box.space.books:insert { 1, 'War and Peace', 1 } + box.space.books:insert { 2, 'Anna Karenina', 1 } + box.space.books:insert { 3, 'Resurrection', 1 } + box.space.books:insert { 4, 'Crime and Punishment', 2 } + box.space.books:insert { 5, 'The Idiot', 2 } + box.space.books:insert { 6, 'The Brothers Karamazov', 2 } + box.space.books:insert { 7, 'Eugene Onegin', 3 } + box.space.books:insert { 8, 'The Captain\'s Daughter', 3 } + box.space.books:insert { 9, 'Boris Godunov', 3 } + box.space.books:insert { 10, 'Ruslan and Ludmila', 3 } + end) +end) + +g.after_all(function(cg) + cg.server:drop() + fio.rmtree(cg.server.workdir) +end) + +g.test_role_granted_revoked = function(cg) + cg.server:exec(function() + box.schema.user.create('testuser', { password = 'foobar' }) + + -- Create roles -- + box.schema.role.create('books_space_manager') + box.schema.role.create('writers_space_reader') + -- End: Create roles -- + + -- Grant read/write privileges to a role -- + box.schema.role.grant('books_space_manager', 'read,write', 'space', 'books') + -- Grant write privileges to a role -- + box.schema.role.grant('writers_space_reader', 'read', 'space', 'writers') + -- End: Grant privileges to roles -- + + -- Grant a role to a role -- + box.schema.role.create('all_spaces_manager') + box.schema.role.grant('all_spaces_manager', 'books_space_manager') + box.schema.role.grant('all_spaces_manager', 'writers_space_reader') + -- End: Grant a role to a role -- + + -- Grant a role to a user -- + box.schema.user.grant('testuser', 'books_space_manager') + box.schema.user.grant('testuser', 'writers_space_reader') + -- End: Grant a role to a user -- + + -- Test removing a tuple from 'writers' -- + box.session.su('testuser') + local _, delete_writer_error = pcall(function() + box.space.writers:delete(3) + end) + t.assert_equals(delete_writer_error:unpack().message, "Write access to space 'writers' is denied for user 'testuser'") + box.session.su('admin') + + -- Revoking a role from a user -- + box.schema.user.revoke('testuser', 'execute', 'role', 'writers_space_reader') + -- End: Revoking a role from a user -- + + -- Test selecting data from 'writers' -- + box.session.su('testuser') + local _, select_writer_error = pcall(function() + box.space.writers:select(3) + end) + t.assert_equals(select_writer_error:unpack().message, "Read access to space 'writers' is denied for user 'testuser'") + box.session.su('admin') + + -- Dropping a role -- + box.schema.role.drop('writers_space_reader') + -- End: Dropping a role -- + + -- Test roles exist -- + t.assert_equals(box.schema.role.exists('books_space_manager'), true) + t.assert_equals(box.schema.role.exists('all_spaces_manager'), true) + t.assert_equals(box.schema.role.exists('writers_space_reader'), false) + end) +end diff --git a/doc/code_snippets/test/access_control/grant_user_privileges_test.lua b/doc/code_snippets/test/access_control/grant_user_privileges_test.lua new file mode 100644 index 0000000000..7e5f1b9e9a --- /dev/null +++ b/doc/code_snippets/test/access_control/grant_user_privileges_test.lua @@ -0,0 +1,138 @@ +local fio = require('fio') +local server = require('luatest.server') +local t = require('luatest') +local g = t.group() +g.before_all(function(cg) + cg.server = server:new { + box_cfg = {}, + workdir = fio.cwd() .. '/tmp' + } + cg.server:start() + cg.server:exec(function() + box.schema.space.create('writers') + box.space.writers:format({ + { name = 'id', type = 'unsigned' }, + { name = 'name', type = 'string' } + }) + box.space.writers:create_index('primary', { parts = { 'id' } }) + + box.schema.space.create('books') + box.space.books:format({ + { name = 'id', type = 'unsigned' }, + { name = 'title', type = 'string' }, + { name = 'author_id', foreign_key = { space = 'writers', field = 'id' } }, + }) + box.space.books:create_index('primary', { parts = { 'id' } }) + + box.space.writers:insert { 1, 'Leo Tolstoy' } + box.space.writers:insert { 2, 'Fyodor Dostoevsky' } + box.space.writers:insert { 3, 'Alexander Pushkin' } + + box.space.books:insert { 1, 'War and Peace', 1 } + box.space.books:insert { 2, 'Anna Karenina', 1 } + box.space.books:insert { 3, 'Resurrection', 1 } + box.space.books:insert { 4, 'Crime and Punishment', 2 } + box.space.books:insert { 5, 'The Idiot', 2 } + box.space.books:insert { 6, 'The Brothers Karamazov', 2 } + box.space.books:insert { 7, 'Eugene Onegin', 3 } + box.space.books:insert { 8, 'The Captain\'s Daughter', 3 } + box.space.books:insert { 9, 'Boris Godunov', 3 } + box.space.books:insert { 10, 'Ruslan and Ludmila', 3 } + end) +end) + +g.after_each(function(cg) + cg.server:exec(function() + if box.schema.user.exists('testuser') then + box.schema.user.drop('testuser') + end + end) +end) + +g.after_all(function(cg) + cg.server:drop() + fio.rmtree(cg.server.workdir) +end) + +g.test_user_without_password_created = function(cg) + cg.server:exec(function() + -- Create a user without a password -- + box.schema.user.create('testuser') + -- End: Create a user without a password -- + t.assert_equals(box.space._user.index.name:select { 'testuser' }[1][5]['chap-sha1'], nil) + end) +end + +g.test_user_with_password_created = function(cg) + cg.server:exec(function() + -- Create a user with a password -- + box.schema.user.create('testuser', { password = 'foobar' }) + -- End: Create a user with a password -- + t.assert_equals(box.space._user.index.name:select { 'testuser' }[1][5]['chap-sha1'], 'm1ADQ7xS4pERcutSrlz0hHYExuU=') + end) +end + +g.test_current_user_password_set = function(cg) + cg.server:exec(function() + box.session.su('admin') + -- Set a password for the current user -- + box.schema.user.passwd('foobar') + -- End: Set a password for the current user -- + t.assert_equals(box.space._user.index.name:select { 'admin' }[1][5]['chap-sha1'], 'm1ADQ7xS4pERcutSrlz0hHYExuU=') + end) +end + +g.test_specified_user_password_set = function(cg) + cg.server:exec(function() + box.schema.user.create('testuser') + -- Set a password for the specified user -- + box.schema.user.passwd('testuser', 'foobar') + -- End: Set a password for the specified user -- + t.assert_equals(box.space._user.index.name:select { 'testuser' }[1][5]['chap-sha1'], 'm1ADQ7xS4pERcutSrlz0hHYExuU=') + end) +end + +g.test_grant_revoke_privileges_user = function(cg) + cg.server:exec(function() + box.schema.user.create('testuser', { password = 'foobar' }) + box.schema.user.grant('testuser', 'execute', 'universe') + -- Grant privileges to the specified user -- + box.schema.user.grant('testuser', 'read', 'space', 'writers') + box.schema.user.grant('testuser', 'read,write', 'space', 'books') + -- End: Grant privileges to the specified user -- + box.session.su('testuser') + local _, delete_writer_error = pcall(function() + box.space.writers:delete(3) + end) + t.assert_equals(delete_writer_error:unpack().message, "Write access to space 'writers' is denied for user 'testuser'") + + box.session.su('admin') + -- Revoke space reading -- + box.schema.user.revoke('testuser', 'write', 'space', 'books') + -- End: Revoke space reading -- + box.session.su('testuser') + local _, delete_book_error = pcall(function() + box.space.books:delete(10) + end) + t.assert_equals(delete_book_error:unpack().message, "Write access to space 'books' is denied for user 'testuser'") + + box.session.su('admin') + -- Revoke session -- + box.schema.user.revoke('testuser', 'session', 'universe') + -- End: Revoke session -- + local _, change_user_error = pcall(function() + box.session.su('testuser') + end) + t.assert_equals(change_user_error:unpack().message, "Session access to universe '' is denied for user 'testuser'") + end) +end + +g.test_user_dropped = function(cg) + cg.server:exec(function() + box.schema.user.create('testuser') + -- Drop a user -- + box.schema.user.drop('testuser') + -- End: Drop a user -- + t.assert_equals(box.schema.user.exists('testuser'), false) + end) +end diff --git a/doc/reference/reference_lua/box_schema/role_create.rst b/doc/reference/reference_lua/box_schema/role_create.rst index 3c2b385bdb..412b2dd0b5 100644 --- a/doc/reference/reference_lua/box_schema/role_create.rst +++ b/doc/reference/reference_lua/box_schema/role_create.rst @@ -22,7 +22,10 @@ box.schema.role.create() **Example:** - .. code-block:: lua + .. literalinclude:: /code_snippets/test/access_control/grant_roles_test.lua + :language: lua + :start-after: Create roles + :end-before: End: Create roles + :dedent: - box.schema.role.create('Accountant') - box.schema.role.create('Accountant', {if_not_exists = false}) + See also: :ref:`access_control_roles`. diff --git a/doc/reference/reference_lua/box_schema/role_drop.rst b/doc/reference/reference_lua/box_schema/role_drop.rst index 9dc22facd7..c33f4961d2 100644 --- a/doc/reference/reference_lua/box_schema/role_drop.rst +++ b/doc/reference/reference_lua/box_schema/role_drop.rst @@ -18,6 +18,10 @@ box.schema.role.drop() **Example:** - .. code-block:: lua + .. literalinclude:: /code_snippets/test/access_control/grant_roles_test.lua + :language: lua + :start-after: Dropping a role + :end-before: End: Dropping a role + :dedent: - box.schema.role.drop('Accountant') + See also: :ref:`access_control_roles`. diff --git a/doc/reference/reference_lua/box_schema/role_exists.rst b/doc/reference/reference_lua/box_schema/role_exists.rst index cfe3afed57..c4d5184d00 100644 --- a/doc/reference/reference_lua/box_schema/role_exists.rst +++ b/doc/reference/reference_lua/box_schema/role_exists.rst @@ -13,8 +13,4 @@ box.schema.role.exists() :param string role-name: the name of the role :rtype: bool - **Example:** - - .. code-block:: lua - - box.schema.role.exists('Accountant') + See also: :ref:`access_control_roles_info`. diff --git a/doc/reference/reference_lua/box_schema/role_grant.rst b/doc/reference/reference_lua/box_schema/role_grant.rst index eaf8ef3179..99f9789a70 100644 --- a/doc/reference/reference_lua/box_schema/role_grant.rst +++ b/doc/reference/reference_lua/box_schema/role_grant.rst @@ -12,18 +12,17 @@ box.schema.role.grant() Grant :ref:`privileges ` to a role. - :param string role-name: the name of the role. - :param string privilege: 'read' or 'write' or 'execute' or 'create' or - 'alter' or 'drop' or a combination. - :param string object-type: 'space' or 'function' or 'sequence' or 'role'. - :param string object-name: the name of a function or space or sequence or role. + :param string role-name: the name of the role + :param string privilege: one or more :ref:`privileges ` to grant to the role (for example, ``read`` or ``read,write``) + :param string object-type: a database :ref:`object type ` to grant privileges to (for example, ``space``, ``role``, or ``function``) + :param string object-name: the name of a function or space or sequence or role :param table option: ``if_not_exists`` = ``true|false`` (default = ``false``) - boolean; ``true`` means there should be no error if the role already - has the privilege. + has the privilege The role must exist, and the object must exist. - **Variation:** instead of ``object-type, object-name`` say 'universe' + **Variation:** instead of ``object-type, object-name`` say ``universe`` which means 'all object-types and all objects'. In this case, object name is omitted. **Variation:** instead of ``privilege, object-type, object-name`` say @@ -31,10 +30,10 @@ box.schema.role.grant() **Example:** - .. code-block:: lua + .. literalinclude:: /code_snippets/test/access_control/grant_roles_test.lua + :language: lua + :start-after: Grant read/write privileges to a role + :end-before: Grant write privileges to a role + :dedent: - box.schema.role.grant('Accountant', 'read', 'space', 'tester') - box.schema.role.grant('Accountant', 'execute', 'function', 'f') - box.schema.role.grant('Accountant', 'read,write', 'universe') - box.schema.role.grant('public', 'Accountant') - box.schema.role.grant('role1', 'role2', nil, nil, {if_not_exists=false}) + See also: :ref:`access_control_roles`. diff --git a/doc/reference/reference_lua/box_schema/role_info.rst b/doc/reference/reference_lua/box_schema/role_info.rst index 76e29d7948..f9465df8d0 100644 --- a/doc/reference/reference_lua/box_schema/role_info.rst +++ b/doc/reference/reference_lua/box_schema/role_info.rst @@ -12,8 +12,4 @@ box.schema.role.info() :param string role-name: the name of the role. - **Example:** - - .. code-block:: lua - - box.schema.role.info('Accountant') + See also: :ref:`access_control_roles_info`. diff --git a/doc/reference/reference_lua/box_schema/role_revoke.rst b/doc/reference/reference_lua/box_schema/role_revoke.rst index 544e44ca35..f9cf898af5 100644 --- a/doc/reference/reference_lua/box_schema/role_revoke.rst +++ b/doc/reference/reference_lua/box_schema/role_revoke.rst @@ -10,26 +10,18 @@ box.schema.role.revoke() Revoke :ref:`privileges ` from a role. - :param string role-name: the name of the role. - :param string privilege: 'read' or 'write' or 'execute' or 'create' or - 'alter' or 'drop' or a combination. - :param string object-type: 'space' or 'function' or 'sequence' or 'role'. - :param string object-name: the name of a function or space or sequence or role. + :param string role-name: the name of the role + :param string privilege: one or more :ref:`privileges ` to revoke from the role (for example, ``read`` or ``read,write``) + :param string object-type: a database :ref:`object type ` to revoke privileges from (for example, ``space``, ``role``, or ``function``) + :param string object-name: the name of a database object to revoke privileges from The role must exist, and the object must exist, but it is not an error if the role does not have the privilege. - **Variation:** instead of ``object-type, object-name`` say 'universe' + **Variation:** instead of ``object-type, object-name`` say ``universe`` which means 'all object-types and all objects'. **Variation:** instead of ``privilege, object-type, object-name`` say ``role-name``. - **Example:** - - .. code-block:: lua - - box.schema.role.revoke('Accountant', 'read', 'space', 'tester') - box.schema.role.revoke('Accountant', 'execute', 'function', 'f') - box.schema.role.revoke('Accountant', 'read,write', 'universe') - box.schema.role.revoke('public', 'Accountant') + See also: :ref:`access_control_roles`. diff --git a/doc/reference/reference_lua/box_schema/user_create.rst b/doc/reference/reference_lua/box_schema/user_create.rst index 38b45b610a..2fec27a7c5 100644 --- a/doc/reference/reference_lua/box_schema/user_create.rst +++ b/doc/reference/reference_lua/box_schema/user_create.rst @@ -21,7 +21,7 @@ box.schema.user.create() * ``password`` (default = '') - string; the ``password`` = *password* specification is good because in a :ref:`URI ` (Uniform Resource Identifier) it is usually illegal to include a - user-name without a password. + username without a password. .. NOTE:: @@ -34,8 +34,10 @@ box.schema.user.create() **Examples:** - .. code-block:: lua + .. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Create a user with a password + :end-before: End: Create a user with a password + :dedent: - box.schema.user.create('testuser') - box.schema.user.create('testuser', {password = 'foobar'}) - box.schema.user.create('testuser', {if_not_exists = false}) + See also: :ref:`access_control_users`. diff --git a/doc/reference/reference_lua/box_schema/user_drop.rst b/doc/reference/reference_lua/box_schema/user_drop.rst index 770c646603..00190a5707 100644 --- a/doc/reference/reference_lua/box_schema/user_drop.rst +++ b/doc/reference/reference_lua/box_schema/user_drop.rst @@ -6,20 +6,23 @@ box.schema.user.drop() .. module:: box.schema -.. function:: box.schema.user.drop(user-name [, {options}]) +.. function:: box.schema.user.drop(username [, {options}]) Drop a user. For explanation of how Tarantool maintains user data, see section :ref:`Users ` and reference on :ref:`_user ` space. - :param string user-name: the name of the user + :param string username: the name of the user :param table options: ``if_exists`` = ``true|false`` (default = ``false``) - boolean; ``true`` means there should be no error if the user does not exist. **Examples:** - .. code-block:: lua + .. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Drop a user + :end-before: End: Drop a user + :dedent: - box.schema.user.drop('Lena') - box.schema.user.drop('Lena',{if_exists=false}) + See also: :ref:`access_control_users`. diff --git a/doc/reference/reference_lua/box_schema/user_exists.rst b/doc/reference/reference_lua/box_schema/user_exists.rst index ec73938a69..4c73651523 100644 --- a/doc/reference/reference_lua/box_schema/user_exists.rst +++ b/doc/reference/reference_lua/box_schema/user_exists.rst @@ -6,18 +6,14 @@ box.schema.user.exists() .. module:: box.schema -.. function:: box.schema.user.exists(user-name) +.. function:: box.schema.user.exists(username) Return ``true`` if a user exists; return ``false`` if a user does not exist. For explanation of how Tarantool maintains user data, see section :ref:`Users ` and reference on :ref:`_user ` space. - :param string user-name: the name of the user + :param string username: the name of the user :rtype: bool - **Example:** - - .. code-block:: lua - - box.schema.user.exists('Lena') + See also: :ref:`access_control_user_info`. diff --git a/doc/reference/reference_lua/box_schema/user_grant.rst b/doc/reference/reference_lua/box_schema/user_grant.rst index 54b18f80b1..a5a1e36322 100644 --- a/doc/reference/reference_lua/box_schema/user_grant.rst +++ b/doc/reference/reference_lua/box_schema/user_grant.rst @@ -6,16 +6,16 @@ box.schema.user.grant() .. module:: box.schema -.. function:: box.schema.user.grant(user-name, privileges, object-type, object-name[, {options} ]) - box.schema.user.grant(user-name, privileges, 'universe'[, nil, {options} ]) - box.schema.user.grant(user-name, role-name[, nil, nil, {options} ]) +.. function:: box.schema.user.grant(username, privileges, object-type, object-name[, {options} ]) + box.schema.user.grant(username, privileges, 'universe'[, nil, {options} ]) + box.schema.user.grant(username, role-name[, nil, nil, {options} ]) Grant :ref:`privileges ` to a user or to another role. - :param string user-name: the name of a user to grant privileges to - :param string privileges: one or more privileges to grant to the user (for example, `read` or `read,write`) - :param string object-type: a database object type to grant privileges to (for example, `space`, `role`, or `function`) + :param string username: the name of a user to grant privileges to + :param string privileges: one or more :ref:`privileges ` to grant to the user (for example, ``read`` or ``read,write``) + :param string object-type: a database :ref:`object type ` to grant privileges to (for example, ``space``, ``role``, or ``function``) :param string object-name: the name of a database object to grant privileges to :param string role-name: the name of a role to grant to the user :param table options: ``grantor``, ``if_not_exists`` @@ -23,15 +23,15 @@ box.schema.user.grant() If :samp:`'function','{object-name}'` is specified, then a _func tuple with that object-name must exist. - **Variation:** instead of ``object-type, object-name`` say 'universe' which + **Variation:** instead of ``object-type, object-name`` say ``universe`` which means 'all object-types and all objects'. In this case, object name is omitted. **Variation:** instead of ``privilege, object-type, object-name`` say ``role-name`` (see section :ref:`Roles `). **Variation:** instead of - :samp:`box.schema.user.grant('{user-name}','usage,session','universe',nil,` :code:`{if_not_exists=true})` - say :samp:`box.schema.user.enable('{user-name}')`. + :samp:`box.schema.user.grant('{username}','usage,session','universe',nil,` :code:`{if_not_exists=true})` + say :samp:`box.schema.user.enable('{username}')`. The possible options are: @@ -41,11 +41,10 @@ box.schema.user.grant() **Example:** - .. code-block:: lua + .. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Grant privileges to the specified user + :end-before: End: Grant privileges to the specified user + :dedent: - box.schema.user.grant('Lena', 'read', 'space', 'tester') - box.schema.user.grant('Lena', 'execute', 'function', 'f') - box.schema.user.grant('Lena', 'read,write', 'universe') - box.schema.user.grant('Lena', 'Accountant') - box.schema.user.grant('Lena', 'read,write,execute', 'universe') - box.schema.user.grant('X', 'read', 'universe', nil, {if_not_exists=true}) + See also: :ref:`access_control_users`. diff --git a/doc/reference/reference_lua/box_schema/user_info.rst b/doc/reference/reference_lua/box_schema/user_info.rst index 4d48f80f14..1428a9d1f0 100644 --- a/doc/reference/reference_lua/box_schema/user_info.rst +++ b/doc/reference/reference_lua/box_schema/user_info.rst @@ -6,23 +6,14 @@ box.schema.user.info() .. module:: box.schema -.. function:: box.schema.user.info([user-name]) +.. function:: box.schema.user.info([username]) Return a description of a user's :ref:`privileges `. - :param string user-name: the name of the user. - This is optional; if it is not - supplied, then the information - will be for the user who is - currently logged in. + :param string username: the name of the user. + This is optional; if it is not + supplied, then the information + will be for the user who is + currently logged in. - **Example:** - - .. code-block:: tarantoolsession - - tarantool> box.schema.user.info('admin') - --- - - - - read,write,execute,session,usage,create,drop,alter,reference,trigger,insert,update,delete - - universe - - - ... + See also: :ref:`access_control_user_info`. diff --git a/doc/reference/reference_lua/box_schema/user_passwd.rst b/doc/reference/reference_lua/box_schema/user_passwd.rst index 1012723d55..4673b594a3 100644 --- a/doc/reference/reference_lua/box_schema/user_passwd.rst +++ b/doc/reference/reference_lua/box_schema/user_passwd.rst @@ -6,22 +6,25 @@ box.schema.user.passwd() .. module:: box.schema -.. function:: box.schema.user.passwd([name,] new_password) +.. function:: box.schema.user.passwd([username,] password) Sets a password for a currently logged in or a specified user: - * A currently logged in user can change their password using - ``box.schema.user.passwd(new_password)``. + * A currently logged-in user can change their password using + ``box.schema.user.passwd(password)``. * An administrator can change the password of another user with - ``box.schema.user.passwd(name, new_password)``. + ``box.schema.user.passwd(username, password)``. - :param string user-name: name - :param string password: new_password + :param string username: a username + :param string password: a new password **Example:** - .. code-block:: lua + .. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Set a password for the specified user + :end-before: End: Set a password for the specified user + :dedent: - box.schema.user.passwd('foobar') - box.schema.user.passwd('testuser', 'foobar') + See also: :ref:`access_control_users`. diff --git a/doc/reference/reference_lua/box_schema/user_revoke.rst b/doc/reference/reference_lua/box_schema/user_revoke.rst index fdbc2aa91a..1672262e27 100644 --- a/doc/reference/reference_lua/box_schema/user_revoke.rst +++ b/doc/reference/reference_lua/box_schema/user_revoke.rst @@ -6,19 +6,18 @@ box.schema.user.revoke() .. module:: box.schema -.. function:: box.schema.user.revoke(user-name, privileges, object-type, object-name[, {options} ]) - box.schema.user.revoke(user-name, privileges, 'universe'[, nil, {options} ]) - box.schema.user.revoke(user-name, role-name[, nil, nil, {options} ]) +.. function:: box.schema.user.revoke(username, privileges, object-type, object-name[, {options} ]) + box.schema.user.revoke(username, privileges, 'universe'[, nil, {options} ]) + box.schema.user.revoke(username, role-name[, nil, nil, {options} ]) Revoke :ref:`privileges ` from a user or from another role. - :param string user-name: the name of the user. - :param string privilege: 'read' or 'write' or 'execute' or 'create' or - 'alter' or 'drop' or a combination. - :param string object-type: 'space' or 'function' or 'sequence'. - :param string object-name: the name of a function or space or sequence. - :param table options: ``if_exists``. + :param string username: the name of the user + :param string privilege: one or more :ref:`privileges ` to revoke from the user (for example, ``read`` or ``read,write``) + :param string object-type: a database :ref:`object type ` to revoke privileges from (for example, ``space``, ``role``, or ``function``) + :param string object-name: the name of a database object to revoke privileges from + :param table options: ``if_exists`` The user must exist, and the object must exist, but if the option setting is ``{if_exists=true}`` then @@ -31,14 +30,15 @@ box.schema.user.revoke() ``role-name`` (see section :ref:`Roles `). **Variation:** instead of - :samp:`box.schema.user.revoke('{user-name}','usage,session','universe',nil,` :code:`{if_exists=true})` - say :samp:`box.schema.user.disable('{user-name}')`. + :samp:`box.schema.user.revoke('{username}','usage,session','universe',nil,` :code:`{if_exists=true})` + say :samp:`box.schema.user.disable('{username}')`. **Example:** - .. code-block:: lua + .. literalinclude:: /code_snippets/test/access_control/grant_user_privileges_test.lua + :language: lua + :start-after: Revoke space reading + :end-before: End: Revoke space reading + :dedent: - box.schema.user.revoke('Lena', 'read', 'space', 'tester') - box.schema.user.revoke('Lena', 'execute', 'function', 'f') - box.schema.user.revoke('Lena', 'read,write', 'universe') - box.schema.user.revoke('Lena', 'Accountant') + See also: :ref:`access_control_users`. diff --git a/doc/reference/reference_lua/box_space/_user.rst b/doc/reference/reference_lua/box_space/_user.rst index fe2ee32687..b3ab6b5847 100644 --- a/doc/reference/reference_lua/box_space/_user.rst +++ b/doc/reference/reference_lua/box_space/_user.rst @@ -8,7 +8,8 @@ box.space._user .. data:: _user - ``_user`` is a system space where user-names and password hashes are stored. + ``_user`` is a system space where user names and password hashes are stored. + Learn more about Tarantool's access control system from the :ref:`access_control` topic. Tuples in this space contain the following fields: @@ -76,68 +77,7 @@ box.space._user .. WARNING:: - To change tuples in the ``_user`` space, do not use ordinary ``box.space`` - functions for insert, update, or delete. The ``_user`` space is special, - so there are special functions that have appropriate error checking.\ - - To create a new user, use :doc:`/reference/reference_lua/box_schema/user_create`: - - .. code-block:: lua - - box.schema.user.create(*user-name*) - box.schema.user.create(*user-name*, {if_not_exists = true}) - box.schema.user.create(*user-name*, {password = *password*}) - - To change the user's password, use :doc:`/reference/reference_lua/box_schema/user_passwd`: - - .. code-block:: lua - - -- To change the current user's password - box.schema.user.passwd(*password*) - - -- To change a different user's password - -- (usually only 'admin' can do it) - box.schema.user.passwd(*user-name*, *password*) - - To drop a user, use :doc:`/reference/reference_lua/box_schema/user_drop`: - - .. code-block:: lua - - box.schema.user.drop(*user-name*) - - To check whether a user exists, use :doc:`/reference/reference_lua/box_schema/user_exists`, - which returns ``true`` or ``false``: - - .. code-block:: lua - - box.schema.user.exists(*user-name*) - - To find what privileges a user has, use :doc:`/reference/reference_lua/box_schema/user_info`: - - .. code-block:: lua - - box.schema.user.info(*user-name*) - - .. NOTE:: - - The maximum number of users is 32. - - **Example:** - - Here is a session which creates a new user with a strong password, selects a - tuple in the ``_user`` space, and then drops the user. - - .. code-block:: tarantoolsession - - tarantool> box.schema.user.create('JeanMartin', {password = 'Iwtso_6_os$$'}) - --- - ... - tarantool> box.space._user.index.name:select{'JeanMartin'} - --- - - - [17, 1, 'JeanMartin', 'user', {'chap-sha1': 't3xjUpQdrt857O+YRvGbMY5py8Q='}] - ... - tarantool> box.schema.user.drop('JeanMartin') - --- - ... + To change tuples in the ``_user`` space, do not use ordinary ``box.space`` functions for insert, update, or delete. + Learn more from :ref:`access_control_users`. The :ref:`system space view ` for ``_user`` is ``_vuser``.