{"id":34645321,"url":"https://github.com/obervinov/users-package","last_synced_at":"2026-02-11T17:07:42.462Z","repository":{"id":62644701,"uuid":"561045373","full_name":"obervinov/users-package","owner":"obervinov","description":"This Python module is designed to simplify user management in Telegram bots, providing the necessary functionality for tasks such as user authentication, authorization and compliance with speed limits, ensuring effective management of user attributes and access rights.","archived":false,"fork":false,"pushed_at":"2025-12-31T10:09:12.000Z","size":360,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-01-03T17:40:53.372Z","etag":null,"topics":["authentication","authorization","limits","module","permissions","poetry","poetry-python","python-module","python3","rate-limits","roles","telegram","telegram-bot","users"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/obervinov.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-11-02T20:29:53.000Z","updated_at":"2025-12-31T10:09:07.000Z","dependencies_parsed_at":"2022-11-04T04:33:56.772Z","dependency_job_id":"795c780f-3ce6-4fc5-a900-3bf68fcab613","html_url":"https://github.com/obervinov/users-package","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"purl":"pkg:github/obervinov/users-package","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obervinov%2Fusers-package","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obervinov%2Fusers-package/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obervinov%2Fusers-package/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obervinov%2Fusers-package/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/obervinov","download_url":"https://codeload.github.com/obervinov/users-package/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/obervinov%2Fusers-package/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29338744,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-11T16:14:43.024Z","status":"ssl_error","status_checked_at":"2026-02-11T16:14:15.258Z","response_time":97,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["authentication","authorization","limits","module","permissions","poetry","poetry-python","python-module","python3","rate-limits","roles","telegram","telegram-bot","users"],"created_at":"2025-12-24T17:40:47.553Z","updated_at":"2026-02-11T17:07:42.457Z","avatar_url":"https://github.com/obervinov.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Users Package\n[![Release](https://github.com/obervinov/users-package/actions/workflows/release.yaml/badge.svg)](https://github.com/obervinov/users-package/actions/workflows/release.yaml)\n[![CodeQL](https://github.com/obervinov/users-package/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/obervinov/users-package/actions/workflows/github-code-scanning/codeql)\n[![Tests and Checks](https://github.com/obervinov/users-package/actions/workflows/pr.yaml/badge.svg)](https://github.com/obervinov/users-package/actions/workflows/pr.yaml)\n\n![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/obervinov/users-package?style=for-the-badge)\n![GitHub last commit](https://img.shields.io/github/last-commit/obervinov/users-package?style=for-the-badge)\n![GitHub Release Date](https://img.shields.io/github/release-date/obervinov/users-package?style=for-the-badge)\n![GitHub issues](https://img.shields.io/github/issues/obervinov/users-package?style=for-the-badge)\n![GitHub repo size](https://img.shields.io/github/repo-size/obervinov/users-package?style=for-the-badge)\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/book.png\" width=\"25\" title=\"about\"\u003e About this project\n**Project Description**\n\nThis Python module is designed to simplify user management in __Telegram Bots__ by providing necessary functionality such as: `authentication`, `authorization` and `request limitation`, providing efficient management of user attributes and access rights.\n\nInteraction Model 1: Using a Unified Entrypoint (Method: `user_access_check()`)\n```mermaid\nsequenceDiagram\n    participant Bot\n    participant Users-Module\n    Bot-\u003e\u003eUsers-Module: Create Users-Module instance with rate limits\n    Bot-\u003e\u003eUsers-Module: Call user_access_check(user_id, role_id)\n    Users-Module--\u003e\u003eBot: Return access, permissions, and rate limits\n```\n\n**Key Features**\n\n- This module is designed primarily for Telegram bots but can be adapted for various projects that require user management, role-based access control, and request rate limiting.\n\n- This module requires certain dependencies related to\n    - [Vault](https://www.vaultproject.io)\n      - [Vault Server](docker-compose.ymla) for storing user configurations and historical data\n      - [Additional Module](https://github.com/obervinov/vault-package ) to interact with the Vault API\n      - [Vault Policy](tests/vault/policy.hcl) with access rights to the Vault Server\n    - [PostgreSQL](https://www.postgresql.org)\n      - [PostgreSQL Server](docker-compose.yml) for storing user data and historical records\n      - [PostgreSQL Schema](tests/postgres/tables.sql) for creating tables in the database\n\n**Table of Contents**\n- [Description of module Constants](#-description-of-module-constants)\n- [Description of module Exceptions](#-description-of-module-exceptions)\n- [Users class](#-users-class)\n- [RateLimiter class](#-ratelimiter-class)\n- [Storage class](#-storage-class)\n- [Token Authentication](#-token-authentication)\n- [Structure of configuration in Vault](#-structure-of-configuration-in-vault)\n- [Structure of historical data in PostgreSQL](#-structure-of-historical-data-in-postgresql)\n- [Additional usage example](#-additional-usage-example)\n- [Installing](#-installing)\n\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/stack2.png\" width=\"21\" title=\"constants\"\u003e Description of module Constants\n\nThis module contains constant values\n\n| Constant Name             | Description                                       | Default Value           |\n|---------------------------|---------------------------------------------------|-------------------------|\n| `USERS_VAULT_CONFIG_PATH` | Path for configuration data in Vault.             | `\"configuration/users\"` |\n| `USER_STATUS_ALLOW`       | User access status for allowed access.            | `\"allowed\"`             |\n| `USER_STATUS_DENY`        | User access status for denied access.             | `\"denied\"`              |\n\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/stack2.png\" width=\"21\" title=\"exceptions\"\u003e Description of module Exceptions\n| Exception                    | Describe                              | Tips |\n|------------------------------|---------------------------------------|------|\n| `WrongUserConfiguration`     | Raised when user configuration is wrong. | Please, see the configuration [example](#-structure-of-configuration-and-statistics-data-in-vault) |\n| `VaultInstanceNotSet`        | Raised when the Vault instance is not set. | Please, see [documentation](#class-initialization) |\n| `FailedDeterminateRateLimit` | Raised when the rate limit cannot be determined. | Please, check misconfiguration between configuration and users requests in PostgreSQL |\n| `StorageInstanceNotSet`      | Raised when the storage instance (PostgreSQL) is not set. | Please, see [documentation](#class-initialization) |\n| `FailedStorageConnection`    | Raised when the connection to the storage (PostgreSQL) failed. | Please, check the connection to the PostgreSQL server |\n\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/build.png\" width=\"25\" title=\"class\"\u003e Users class\n### Class Initialization\n\nThe `Users` class provides authentication, authorization, user attribute management and user request logging for Telegram bots. You can initialize it with different options\n\n- `vault (any)`: Configuration for initializing the Vault client.\n  - `(object)`: an already initialized instance of VaultClient for interacting with the Vault API.\n  - `(dict)`: extended configuration for VaultClient (for database engine).\n    - `instance (VaultClient)`: An already initialized instance for interacting with the Vault API.\n    - `role (str)`: The role name for the Vault database engine.\n\n- `rate_limits (bool)`: Enable rate limit functionality.\n\n- `storage_connection (any)`: Connection object to connect to the storage. Do not use if you are using Vault database engine.\n- **Examples:**\n\n  - Initialize with `VaultClient` and without `rate_limits`:\n    ```python\n    users_without_ratelimits = Users(vault=\u003cVaultClient\u003e, rate_limits=False, storage_connection=psycopg2.connect(**db_config))\n    ```\n\n  - Initialize with `VaultClient` and with `rate_limits`:\n    ```python\n    users_with_ratelimits = Users(vault=\u003cVaultClient\u003e, storage_connection=psycopg2.connect(**db_config))\n    ```\n\n  - Initialize with Vault `configuration dictionary` (for using the vault database engine):\n    ```python\n    vault_config = {'instance': \u003cVaultClient\u003e, 'role': 'my_db_role'}\n    users_with_dict_vault = Users(vault=vault_config)\n    ```\n\n### decorator: Access Control\n\nThe `access_control()` decorator is used to control access to specific functions based on user roles and permissions.\u003c/br\u003e\n**Required the `pyTelegramBotAPI` objects:** `telegram.telegram_types.Message` or `telegram.telegram_types.CallbackQuery`\n\n- **Arguments:**\n  - `role_id (str)`: Required role ID for the specified user ID.\n  - `flow (str)`: The flow of the function, which can be either.\n    - `auth` for authentication. Default value.\n    - `authz` for authorization.\n\n- **Examples:**\u003c/br\u003e\n  Role-based access control\n  ```python\n    @telegram.message_handler(commands=['start'])\n    @access_control(role_id='admin_role', flow='authz')\n    # Decorator returns user information about access, permissions, and rate limits into access_result argument\n    def my_function(message: telegram.telegram_types.Message, access_result: dict = None):\n        print(f\"User permissions: {access_result}\")\n        pass\n  ```\n  Just authentication\n  ```python\n    @telegram.message_handler(commands=['start'])\n    @access_control()\n    # Decorator returns user information about access, permissions, and rate limits into access_result argument\n    def my_function(message: telegram.telegram_types.Message, access_result: dict = None):\n        print(f\"User permissions: {access_result}\")\n        pass\n  ```\n\n- **Returns:**\n  - Breaks the function and returns an error message if the user does not have the required role or permission.\n\n### method: User Access Check\n\nThe `user_access_check()` method is the main entry point for authentication, authorization, and request rate limit verification. It is used to control the request rate (limits) for a specific user.\n\n**New in v4.3.0**: Rate limits can now be calculated independently without requiring `role_id` parameter. This allows rate limiting for authentication-only scenarios.\n\n- **Arguments:**\n  - `user_id (str)`: Required user ID.\n  - `role_id (str)`: Optional role ID for authorization. If provided, authorization will be performed and rate limits calculated after successful authorization. If omitted, only authentication and rate limits (if enabled) will be processed.\n\n- **Keyword Arguments:**\n  - `chat_id (str)`: Required chat ID for the specified user ID. Additional context for logging.\n  - `message_id (str)`: Required message ID for the specified user ID. Additional context for logging.\n\n- **Examples:**\n  \n  **With authorization and rate limits:**\n  ```python\n  user_access_check(user_id='user1', role_id='admin_role', chat_id='chat1', message_id='msg1')\n  # Returns: {'access': 'allowed', 'permissions': 'allowed', 'rate_limits': datetime or None}\n  ```\n  \n  **Authentication and rate limits only (v4.3.0+):**\n  ```python\n  user_access_check(user_id='user1', chat_id='chat1', message_id='msg1')\n  # Returns: {'access': 'allowed', 'rate_limits': datetime or None}\n  ```\n\n- **Returns:**\n  - A dictionary with access status, permissions (if role_id provided), and rate limit information.\n    ```python\n    # With role_id:\n    {\n      'access': self.user_status_allow / self.user_status_deny,\n      'permissions': self.user_status_allow / self.user_status_deny,\n      'rate_limits': '2023-08-06 11:47:09.440933' / None\n    }\n    \n    # Without role_id (v4.3.0+):\n    {\n      'access': self.user_status_allow / self.user_status_deny,\n      'rate_limits': '2023-08-06 11:47:09.440933' / None\n    }\n    ```\n\n### Description of class attributes\n| Data Type | Attribute           | Purpose                                                      | Default Value           |\n|-----------|---------------------|--------------------------------------------------------------|-------------------------|\n| `object`  | `vault`             | Vault instance for interacting with the Vault API.           | `None`                  |\n| `dict`    | `storage`           | Configuration for initializing the storage client.           | `None`                  |\n| `bool`    | `rate_limits`       | Enable request rate limit functionality.                     | `True`                  |\n| `str`     | `user_status_allow` | User access status: allowed.                                 | `\"allowed\"`             |\n| `str`     | `user_status_deny`  | User access status: denied.                                  | `\"denied\"`              |\n| `str`     | `vault_config_path` | The prefix of the configuration path in the Vault.           | `\"configuration/users\"` |\n\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/build.png\" width=\"25\" title=\"class\"\u003e RateLimiter class\n### Class Initialization\n\nThe `RateLimiter` class provides restriction functionality for user requests to the Telegram bot in the context of a specific user.\n\n- `vault (VaultClient)`: An already initialized instance for interacting with the Vault API or a configuration dictionary for initializing a VaultClient instance in this class.\n\n- storage (Storage): An already initialized instance for interacting with the storage (PostgreSQL) or a configuration dictionary for initializing a Storage instance in this class.\n\n- `user_id (str)`: User ID for checking speed limits.\n\n- **Examples:**\n  ```python\n  limiter = RateLimiter(vault=\u003cVaultClient\u003e, storage=storage_client, user_id='User1')\n  ```\n\n### method: Rate Limit Determination\n\nThe `determine_rate_limit()` method is the main entry point for checking bot request limits for the specified user. It returns information about whether the request rate limits are active and when they expire \n\n- **Examples:**\n  ```python\n  determine_rate_limit()\n  ```\n\n- **Returns:**\n  - String with a `timestamp` of the end of restrictions on requests or `None` if rate limit is not applied.\n    ```python\n    (\"2023-08-07 10:39:00.000000\" | None)\n    ```\n\n### method: Get User Requests Counters\n\nThe `get_user_request_counters()` method calculates the number of requests made by the user and returns the number of requests per day and per hour.\n\n- **Examples:**\n  ```python\n  get_user_request_counters()\n  ```\n\n- **Returns:**\n  - A dictionary with the number of requests per day and per hour.\n    ```python\n    {\n      'requests_per_day': 9,\n      'requests_per_hour': 1\n    }\n    ```\n\n### Description of Class Attributes\n| Data Type      | Attribute                | Purpose                                                                  | Default Value                   |\n|----------------|--------------------------|--------------------------------------------------------------------------|---------------------------------|\n| `VaultClient`  | `vault`                  | Vault instance for interacting with the Vault API.                       | `None`                          |\n| `Storage`      | `storage`                | Storage instance for interacting with the storage (PostgreSQL).          | `None`                          |\n| `str`          | `user_id`                | User ID for checking speed limits.                                       | `None`                          |\n| `str`          | `vault_config_path`      | The prefix of the configuration path in the Vault.                       | `\"configuration/users\"`         |\n| `dict`         | `requests_configuration` | User request limits configuration.                                       | `None`                          |\n| `dict`         | `requests_counters`      | Counters for the number of requests per day and per hour.                | `None`                          |\n\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/build.png\" width=\"25\" title=\"class\"\u003e Storage class\n### Class Initialization\nThe storage class for the storage of user data: requests, access logs, etc in the PostgreSQL database.\u003c/br\u003e\n**Only one of the parameters is required for initialization: `db_connection` or `vault`**.\n\n- `db_connection (object)`: The database connection object for interacting with the PostgreSQL database (psycopg2).\n\n- `vault (dict)`: Configuration for initializing the Vault client.\n  - `instance (VaultClient)`: An already initialized instance for interacting with the Vault API.\n  - `role (str)`: The role name for the Vault database engine.\n\n- **Examples:**\n  ```python\n  storage = Storage(db_connection=psycopg2.connect(**db_config))\n  ```\n  ```python\n  storage = Storage(vault={'instance': \u003cVaultClient\u003e, 'role': 'my_db_role'})\n  ```\n\n### method: Create connection to the PostgreSQL database\nThe `create_connection()` method creates a connection to the PostgreSQL database.\n\n- **Examples:**\n```python\ncreate_connection()\n```\n\n- **Returns:**\n  - Connection object to the PostgreSQL database.\n\n### method: Register of User\nThe `register_user()` method registers a new user in the database.\n\n- **Arguments:**\n  - `user_id (str)`: User ID for registration.\n  - `chat_id (str)`: Chat ID for registration.\n  - `status (str)`: The user state in the system. Values: `self.user_status_allow` or `self.user_status_deny`.\n\n- **Examples:**\n  ```python\n  register_user(user_id='user1', chat_id='chat1', status='allowed')\n  ```\n\n### method: Log user request\nThe `log_user_request()` method logs the user request in the database.\n\n- **Arguments:**\n  - `user_id (str)`: User ID for logging.\n  - `request (dict)`: The user request details.\n\n- **Examples:**\n  ```python\n  log_user_request(user_id='user1', request={'chat_id': 'chat1', 'message_id': 'msg1'})\n  ```\n\n### method: Get user requests\nThe `get_user_requests()` method retrieves the user's requests from the database.\n\n- **Arguments:**\n  - `user_id (str)`: User ID for retrieving requests.\n  - `limit (int)`: The number of requests to retrieve.\n  - `order (str)`: The order of the requests. Values: `asc` or `desc`.\n\n- **Examples:**\n  ```python\n  get_user_requests(user_id='user1', limit=10, order='asc')\n  ```\n\n- **Returns:**\n  - A list of user requests `[(id, timestamp, rate_limits), ...]`.\n\n### method: Get users\nThe `get_users()` method retrieves all users from the database.\n\n- **Arguments:**\n  - `only_allowed (bool)`: Retrieve only allowed users.\n\n- **Examples:**\n  ```python\n  get_users()\n  ```\n\n- **Returns:**\n  - A list of users `[{'user_id': '12345', 'chat_id': '67890', 'status': 'denied'}, ...]`.\n\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/config.png\" width=\"25\" title=\"token-authentication\"\u003e Token Authentication\n\n### Overview\nStarting from **v4.2.0**, the Users package supports generic token-based authentication for frontend integration (web UIs, mobile apps, CLI tools). This feature enables temporary access without storing user credentials.\n\n### Use Cases\n- **Database Bridge Pattern**: Telegram bot issues tokens, frontend validates them through Users module using shared PostgreSQL database\n- **Restricted Bot Access**: Alternative authentication when Telegram auth widget is unavailable (e.g., bot with closed external access)\n- **Web Authentication**: Secure temporary access for web interfaces\n- **API Access**: Short-lived tokens for API clients\n- **Mobile Apps**: Authenticate mobile app users without password storage\n\n### Token Format\nTokens follow the format `user_id.token_id`:\n- Example: `\"123456.a8f3kjs9dfjkl23jrlksjdf...\"`\n- Total length: ~45-50 characters\n- Cryptographically secure using PBKDF2 with salt\n\n### Available Methods\n\n#### issue_token(user_id, ttl_minutes=10)\nGenerate a temporary access token for a user.\n\n**Arguments:**\n- `user_id (str)`: User ID to issue token for\n- `ttl_minutes (int)`: Token validity period in minutes (default: 10)\n\n**Returns:** Token string in format `\"user_id.token_id\"`\n\n**Example:**\n```python\ntoken = users.issue_token(user_id='user1', ttl_minutes=15)\nprint(token)  # \"user1.a8f3kjs9dfjkl23jrlksjdf...\"\n```\n\n#### validate_token(token)\nValidate a token and return user information.\n\n**Arguments:**\n- `token (str)`: Token string in format `\"user_id.token_id\"`\n\n**Returns:** \n- `dict`: User info `{'user_id': str, 'status': str, 'roles': list}` if valid\n- `None`: If token is invalid, expired, or already used\n\n**Example:**\n```python\nuser_info = users.validate_token(token=token)\nif user_info:\n    print(f\"User: {user_info['user_id']}\")\n    print(f\"Status: {user_info['status']}\")\n    print(f\"Roles: {user_info['roles']}\")\n```\n\n**Note:** Tokens are single-use and automatically marked as used after validation.\n\n#### revoke_token(user_id)\nRevoke all existing tokens for a user.\n\n**Arguments:**\n- `user_id (str)`: User ID to revoke tokens for\n\n**Example:**\n```python\nusers.revoke_token(user_id='user1')\n```\n\n### Security Features\n- **PBKDF2 Hashing**: Tokens hashed with 100,000 iterations\n- **Salt**: Unique 32-byte salt per token\n- **Single-Use**: Tokens automatically invalidated after first use\n- **Auto-Revocation**: New token issuance revokes previous tokens\n- **Expiration**: Configurable TTL with automatic cleanup\n\n### Database Schema\nTokens stored in `users_tokens` table. See full schema in [tables.sql](tests/postgres/tables.sql).\n\n### Backward Compatibility\nAll token methods gracefully handle missing `users_tokens` table, logging warnings without raising exceptions. Existing deployments continue working without schema updates.\n\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/requirements.png\" width=\"25\" title=\"configuration-structure\"\u003e Structure of configuration in Vault\nThis project uses a Vault server with the KV2 engine and Database Engine for storing user configurations and database connection data.\nIt supports user configurations to define system access rights, roles, and request restrictions.\n\n### Users Configuration\n- **path to the secret**: `configuration/users/{user_id}`\n- **keys and Values**:\n  - `status`: The status of user access, which can be either\n      - `self.user_status_allow`\n      - `self.user_status_deny`\n  - `roles`: A list of roles associated with the user ID, e.g., `[\"role1\", \"role2\"]`.\n  - `requests`: Limits on the number of requests\n      - `requests_per_day`\n      - `requests_per_hour`\n      - `random_shift_time` (additional, random shift in minutes from 0 to the specified number) in minutes\n\n- **example of a secret with configuration**:\n```json\n{\n  \"status\": \"allowed\",\n  \"roles\": [\"admin_role\", \"additional_role\"],\n  \"requests\": {\n    \"requests_per_day\": 10,\n    \"requests_per_hour\": 1,\n    \"random_shift_minutes\": 15\n  }\n}\n```\n\n### Database Configuration\n- **path to the secret**: `configuration/database`\n\n- **keys and values with simple database connection**:\n  - `host`: The host of the PostgreSQL server.\n  - `port`: The port of the PostgreSQL server.\n  - `database`: The name of the PostgreSQL database.\n  - `user`: The username for the PostgreSQL database.\n  - `password`: The password for the PostgreSQL database.\n\n  ```json\n  {\n    \"host\": \"localhost\",\n    \"port\": 5432,\n    \"dbname\": \"mydatabase\",\n    \"user\": \"myuser\",\n    \"password\": \"mypassword\",\n  }\n  ```\n\n- **keys and values with Vault Database Engine**:\n  - `role`: The role name for the Vault database engine.\n  - `instance`: The instance of the VaultClient for interacting with the Vault API.\n\n  ```json\n  {\n    \"host\": \"localhost\",\n    \"port\": 5432,\n    \"dbname\": \"mydatabase\",\n  }\n  ```\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/requirements.png\" width=\"25\" title=\"data-structure\"\u003e Structure of historical data in PostgreSQL\nThis project uses a PostgreSQL database to store historical data about user requests and access events. It supports user request logging to track user activity and access rights.\nThe detailed table schema can be found in this [sql file](tests/postgres/tables.sql).\n\n### Users Requests Table\nContains records of user requests, access permission, access level, and apply limits on the number of requests.\n\n### Users Table\nContains records of user metadata for the Telegram bot, such as user ID, chat ID, and message ID.\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/config.png\" width=\"25\" title=\"usage\"\u003e Additional usage example\nExample 1 - With Rate Limits\n```python\n# import modules\nfrom vault import VaultClient\nfrom users import Users\n\n# create the vault client\nvault_client = VaultClient(\n  url='http://0.0.0.0:8200',\n  namespace='my_project',\n  auth={\n      'type': 'approle',\n      'role_id': 'my_role',\n      'secret_id': 'my_secret_id'\n  }\n)\n\n# create the Users instance of the class with rate limits and get user information\nusers = Users(vault=\u003cVaultClient\u003e, rate_limits=True, storage_connection=psycopg2.connect(**db_config))\nuser_info = users.user_access_check(user_id=message.chat.id, role_id=\"admin_role\", chat_id=message.chat.id, message_id=message.message_id)\n\n# check permissions, roles, and rate limits\nif user_info[\"access\"] == users.user_status_allow:\n    print(\"Hi, you can use the bot!\")\n    if user_info[\"permissions\"] == users.user_status_allow:\n        if user_info[\"rate_limits\"]:\n            print(f\"You have sent too many requests, the limit is applied until {user_info['rate_limits']}\")\n        else:\n            print(\"You have admin's rights\")\n    else:\n        print(\"You do not have access rights to this function\")\nelse:\n    print(\"Access denied, goodbye!\")\n```\n\nExample 2 - Without Rate Limits\n```python\n# import modules\nfrom vault import VaultClient\nfrom users import Users\n\n# create the vault client\nvault_client = VaultClient(\n  url='http://vault.example.com',\n  namespace='my_project',\n  auth={\n      'type': 'approle',\n      'role_id': 'my_role',\n      'secret_id': 'my_secret_id'\n  }\n)\n\n# create the Users instance of the class without rate limits and get user information\nusers = Users(vault=\u003cVaultClient\u003e, storage_connection=psycopg2.connect(**db_config))\nuser_info = users.user_access_check(user_id=message.chat.id, role_id=\"admin_role\", chat_id=message.chat.id, message_id=message.message_id)\n\n# check permissions and roles\nif user_info[\"access\"] == users.user_status_allow:\n    print(\"Hi, you can use the bot!\")\n    if user_info[\"permissions\"] == users.user_status_allow:\n        print(\"You have admin's rights\")\n    else:\n        print(\"You do not have access rights to this function\")\nelse:\n    print(\"Access denied, goodbye!\")\n```\n\nExample 3 - Decorator Usage\n```python\n# import modules\nfrom vault import VaultClient\nfrom users import Users\n\n# create the vault client\nvault_client = VaultClient(\n  url='http://vault.example.com',\n  namespace='my_project',\n  auth={\n      'type': 'approle',\n      'role_id': 'my_role',\n      'secret_id':\n  }\n)\n\n# create the Users instance of the class with rate limits\nusers = Users(vault=\u003cVaultClient\u003e, rate_limits=True, storage_connection=psycopg2.connect(**db_config))\n\n# create a function with the access_control decorator\n@telegram.message_handler(commands=['start'])\n@access_control()\n# Decorator returns user information about access, permissions, and rate limits into access_result argument\ndef my_function(message: telegram.telegram_types.Message, access_result: dict = None):\n    print(f\"User permissions: {access_result}\")\n    pass\n\n# call the function\nmy_function(message)\n```\n\nExample 4 - Token Authentication (v4.2.0+)\n```python\n# import modules\nfrom vault import VaultClient\nfrom users import Users\nimport psycopg2\n\n# create the vault client and users instance\nvault_client = VaultClient(\n  url='http://vault.example.com',\n  namespace='my_project',\n  auth={\n      'type': 'approle',\n      'role_id': 'my_role',\n      'secret_id': 'my_secret_id'\n  }\n)\n\nusers = Users(vault=vault_client, storage_connection=psycopg2.connect(**db_config))\n\n# Issue a temporary access token for a user (e.g., from Telegram bot)\ntoken = users.issue_token(user_id='user1', ttl_minutes=15)\nprint(f\"Token: {token}\")  # Returns: \"user1.a8f3kjs9dfjkl23jrlksjdf...\"\n\n# Validate the token (e.g., in web frontend or API)\nuser_info = users.validate_token(token=token)\nif user_info:\n    print(f\"User authenticated: {user_info['user_id']}\")\n    print(f\"Status: {user_info['status']}\")\n    print(f\"Roles: {user_info['roles']}\")\nelse:\n    print(\"Invalid or expired token\")\n\n# Revoke all tokens for a user\nusers.revoke_token(user_id='user1')\n```\n\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/stack2.png\" width=\"20\" title=\"install\"\u003e Installing\n```bash\ntee -a pyproject.toml \u003c\u003cEOF\n[tool.poetry]\nname = myproject\"\nversion = \"1.0.0\"\ndescription = \"\"\n\n[tool.poetry.dependencies]\npython = \"^3.12\"\nusers = { git = \"https://github.com/obervinov/users-package.git\", tag = \"v4.1.3\" }\n\n[build-system]\nrequires = [\"poetry-core\"]\nbuild-backend = \"poetry.core.masonry.api\"\nEOF\n\npoetry install\n```\n\n## \u003cimg src=\"https://github.com/obervinov/_templates/blob/v1.0.5/icons/github-actions.png\" width=\"25\" title=\"github-actions\"\u003e GitHub Actions\n| Name  | Version |\n| ------------------------ | ----------- |\n| GitHub Actions Templates | [v2.1.1](https://github.com/obervinov/_templates/tree/v2.1.1) |","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobervinov%2Fusers-package","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fobervinov%2Fusers-package","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fobervinov%2Fusers-package/lists"}