{"id":19003437,"url":"https://github.com/pancakeconnaisseur/user_backend_sql_raw","last_synced_at":"2025-04-22T18:14:37.869Z","repository":{"id":32082810,"uuid":"129643450","full_name":"PanCakeConnaisseur/user_backend_sql_raw","owner":"PanCakeConnaisseur","description":"User management and authentication through arbitrary SQL queries for Nextcloud","archived":false,"fork":false,"pushed_at":"2024-07-16T13:43:21.000Z","size":610,"stargazers_count":21,"open_issues_count":13,"forks_count":15,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-04-17T09:00:33.610Z","etag":null,"topics":["authentication","mariadb","mysql","nextcloud","postgresql","sql","sql-queries","sql-user-backend","user-management"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/PanCakeConnaisseur.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":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-04-15T19:30:14.000Z","updated_at":"2025-03-11T10:22:55.000Z","dependencies_parsed_at":"2024-05-04T10:28:02.477Z","dependency_job_id":"e3e8bf0f-9452-4576-9e2c-4905c1281844","html_url":"https://github.com/PanCakeConnaisseur/user_backend_sql_raw","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PanCakeConnaisseur%2Fuser_backend_sql_raw","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PanCakeConnaisseur%2Fuser_backend_sql_raw/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PanCakeConnaisseur%2Fuser_backend_sql_raw/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/PanCakeConnaisseur%2Fuser_backend_sql_raw/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/PanCakeConnaisseur","download_url":"https://codeload.github.com/PanCakeConnaisseur/user_backend_sql_raw/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250296202,"owners_count":21407037,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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","mariadb","mysql","nextcloud","postgresql","sql","sql-queries","sql-user-backend","user-management"],"created_at":"2024-11-08T18:19:07.481Z","updated_at":"2025-04-22T18:14:37.814Z","avatar_url":"https://github.com/PanCakeConnaisseur.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# User Backend SQL Raw\n\n[![Latest Release by Semantic Version)](https://img.shields.io/github/v/release/PanCakeConnaisseur/user_backend_sql_raw?sort=semver)](https://github.com/PanCakeConnaisseur/user_backend_sql_raw/releases)\n[![Total Downloads](https://img.shields.io/github/downloads/PanCakeConnaisseur/user_backend_sql_raw/total)](https://github.com/PanCakeConnaisseur/user_backend_sql_raw/releases)\n![Tests Status](https://img.shields.io/github/actions/workflow/status/PanCakeConnaisseur/user_backend_sql_raw/tests.yml?branch=master)\n\nThis is an app for Nextcloud that offers user management and authentication with\narbitrary SQL queries.\n\nYou can authenticate, create, delete users, change their password or display\nname, basically do (almost) everything that Nextcloud can do with users.\n\nIn contrast to the app *SQL user backend*, you write the SQL queries yourself.\nYou are not limited by assumptions that app authors made about how your db is\nstructured.\n\nThe app uses prepared statements and is written to be secure by default to\nprevent SQL injections. It understands the most popular standards for password\nhash formats: MD5-CRYPT, SHA256-CRYPT, SHA512-CRYPT, BCrypt, Argon2i and\nArgon2id. Because the various formats are recognized on-the-fly your db can can\nhave differing hash string formats at the same time, which eases migration to\nnewer formats.\n\nThis app primarily supports PostgreSQL and MariaDB/MySQL but the underlying PHP\n[mechanism](https://www.php.net/manual/en/pdo.drivers.php) also supports\nFirebird, MS SQL, Oracle DB, ODBC, DB2, SQLite, Informix and IBM databases. By\nusing an appropriate DSN you should be able to connect to these databases. This\nhas not been tested, though.\n\nSee [CHANGELOG.md](CHANGELOG.md) for changes in newer versions. This app follows\nsemantic versioning and there should not be any breaking changes unless the\nmajor version has changed.\n\n## Installation\n\nYou can find *User Backend SQL Raw* in the *Security* category of the Nextcloud\napp store inside your Nextcloud instance.\n\n## Configuration\n\nThis app has no user interface. All configuration is done via Nextcloud's system\n configuration in *config/config.php*. This app uses the config key\n `user_backend_sql_raw`. The following code shows a complete configuration with\n all optional parameters commented out.\n\n```php\n 'user_backend_sql_raw' =\u003e array(\n   'dsn' =\u003e 'pgsql:host=/var/run/postgresql;dbname=theNameOfYourUserDb',\n   //'db_user' =\u003e 'yourDatabaseUser',\n   //'db_password' =\u003e 'thePasswordForTheDatabaseUser',\n   //'db_password_file' =\u003e '/path/to/file/ContainingThePasswordForTheDatabaseUser',\n   'queries' =\u003e array(\n       'get_password_hash_for_user' =\u003e 'SELECT password_hash FROM users_fqda WHERE fqda = :username',\n       'user_exists' =\u003e 'SELECT EXISTS(SELECT 1 FROM users_fqda WHERE fqda = :username)',\n       'get_users' =\u003e 'SELECT fqda FROM users_fqda WHERE (fqda ILIKE :search) OR (display_name ILIKE :search)',\n       //'set_password_hash_for_user' =\u003e 'UPDATE users SET password_hash = :new_password_hash WHERE local = split_part(:username, \\'@\\', 1) AND domain = split_part(:username, \\'@\\', 2)',\n       //'delete_user' =\u003e 'DELETE FROM users WHERE local = split_part(:username, \\'@\\', 1) AND domain = split_part(:username, \\'@\\', 2)',\n       //'get_display_name' =\u003e 'SELECT display_name FROM users WHERE local = split_part(:username, \\'@\\', 1) AND domain = split_part(:username, \\'@\\', 2)',\n       //'set_display_name' =\u003e 'UPDATE users SET display_name = :new_display_name WHERE local = split_part(:username, \\'@\\', 1) AND domain = split_part(:username, \\'@\\', 2)',\n       //'count_users' =\u003e 'SELECT COUNT (*) FROM users',\n       //'get_home' =\u003e '',\n       //'create_user' =\u003e 'INSERT INTO users (local, domain, password_hash) VALUES (split_part(:username, \\'@\\', 1), split_part(:username, \\'@\\', 2), :password_hash)',\n  ),\n //'hash_algorithm_for_new_passwords' =\u003e 'bcrypt',\n ),\n ```\n\nThere are three types of configuration parameters:\n\n### 1. Database\n\nthat *User Backend SQL Raw* will connect to.\n\n* `dsn`: check how to construct DSNs for [PostgreSQL](https://www.php.net/manual/en/ref.pdo-pgsql.connection.php) and [MySQL](https://www.php.net/manual/en/ref.pdo-mysql.connection.php).\n* `db_user`: user that will be used to connect to the database\n* `db_password`: password for the user that will be used to connect to the database\n* `db_password_file`: Can be set to read the password from a file\n  * Only the first line of the file specified by `db_password_file` is read.\n  * Not more than 100 characters of the first line are read.\n  * Whitespace-like characters are [trimmed](https://www.php.net/manual/en/function.trim.php) from\n    the beginning and end of the read password.\n\nThere are two methods to configure the database connection:\n\n1. Set `dsn` to a DSN that contains the entire db connnection configuration including the db user and db password\n2. Set `dsn` to a DSN that contains everything **but** the db user and db password and then set `db_user` and `db_password`/`db_password_file`\n\nPostgreSQL works with method 1 and 2. MySQL works only with method 2. If you use `db_password_file` also set `db_user` (even for PostgreSQL) and don't put the username in the DSN. This is because, the underlying PDO classes have some quirks and diverge from the documented behaviour. So, better don't mix both methods. `db_password_file` has higher priority than `db_password`, but lower priority than password in DSN. But it's better to only set one source for the password, for the same reasons.\n\n#### Examples\n\n* connect to PostgreSQL via a socket with ident authentication which requires no user or password at all:\n\n  ```php\n  'dsn' =\u003e 'pgsql:host=/var/run/postgresql;dbname=theNameOfYourUserDb',\n  ```\n\n* connect to PostgreSQL via TCP and user/password authentication:\n  ```php\n  'dsn' =\u003e 'pgsql:host=localhost;port=5432;dbname=theNameOfYourUserDb;user=theNameOfYourDbUser;password=thePasswordForTheDbUser',\n  ```\n* connect to PostgreSQL via TCP and user/password authentication and use password file:\n\n  ```php\n  'dsn' =\u003e 'pgsql:host=localhost;port=5432;dbname=theNameOfYourUserDb',\n  'db_user' =\u003e 'theNameOfYourDbUser',\n  'db_password_file' =\u003e '/path/to/password_file',\n  ```\n\n* connect to MySQL via socket which requires no user or password at all:\n\n  ```php\n  'dsn' =\u003e 'mysql:unix_socket=/var/run/mysql/mysql.sock;dbname=theNameOfYourUserDb',\n  ```\n  \n* connect to MySQL via TCP and user/password authentication:\n\n  ```php\n  'dsn' =\u003e 'mysql:host=localhost;port=3306;dbname=testdb',\n  'db_user' =\u003e 'theNameOfYourDbUser',\n  'db_password' =\u003e 'thePasswordForTheDbUser', // or db_password_file instead\n  ```\n\nFor other databases check their [PDO driver documentation pages](https://www.php.net/manual/en/pdo.drivers.php) which in-turn link to their respective DSN references. They either use method 1 or method 2 AFAICS.\n\n### 2. SQL Queries\n\nthat will be used to read/write data.\n\n* queries use named parameters. You have to use the exact names as shown in the examples. For\n example, to retrieve the hash for a user, the query named `get_password_hash_for_user` will be\n used. Write your custom SQL query and simply put `:username` where you are referring to the\n username (aka uid) of the user trying to login.\n* You don't need to supply all queries. For example, if you use the default user home simply leave\n the query `get_home` commented. This app will recognize this and\n [communicate](https://github.com/nextcloud/server/blob/316acc3cc313f4333fe29d136f9124f163b40dec/lib/public/UserInterface.php#L47)\n to Nextcloud that this feature is not available.\n  * `user_exists` and `get_users` are required, the rest is optional.\n  * For user authentication (i.e. login) you need at least `get_password_hash_for_user`,\n    `user_exists` and `get_users`.\n* For all queries that read data, only the first column is interpreted.\n* Two queries require a little bit of attention:\n    1. `user_exists` should return a boolean. See the example on how to do this properly.\n    2. `get_users` is a query that searches for usernames (e.g. *bob*) and display names (e.g. *Bob\n       Bobson*) and returns usernames\n        * make sure the query looks through both usernames **and** display names, see example config\n        * do case insensitive pattern matching, i.e. `ILIKE` (`ILIKE` only available in PostgreSQL)\n        * query must not already contain a `LIMIT` or `OFFSET`. They will be added to the end of\n          your query by this app\n        * specify the `LIKE` without `%`, they will be added by the app. This is due to how prepared\n          statements work. Again, see the example.\n* Technical Info: Queries are passed verbatim to the\n    [prepare()](http://php.net/manual/en/pdo.prepare.php) method of a PDO object.\n\n### 3. Hash Algorithm For New Passwords\n\nused for the creation of new passwords.\n\n* is optional and, if you leave it empty, defaults to `bcrypt` ($2y$).\n* Other supported hash algorithms are MD5-CRYPT, SHA-256-CRYPT, SHA-512-CRYPT, Argon2i and Argon2id.\nThe config values are `md5`, `sha256`, `sha512`, `argon2i`, `argon2id` respectively, e.g.\n  `'hash_algorithm_for_new_passwords' =\u003e 'argon2id',`. Or you can explicitly set `bcrypt`.\n* This parameter only sets the hash algorithm for the creation of new passwords. For\n checking an existing password the hash algorithm will be [detected automatically](http://php.net/manual/en/function.password-verify.php)\n and all common crypt formats are recognized.\n  * This means, that your db can have different hash formats simultaneously. Whenever a\n    user's password is changed, it will be updated to the configured hash algorithm. This eases\n     migration to more modern algorithms.\n\n## Security\n\n* Password length is limited to 100 characters to prevent denial of service attacks against the\nweb server. Without a limit, malicious users could feed your Nextcloud instance with passwords that have a length of tens of thousands of characters, which could cause a very\n high load due to expensive password hashing operations.\n* The username during user creation (`create_user`) and the display name (`set_display_name`) are\n not limited in length. You should limit this on the db layer.\n\n## Troubleshooting\n\n* **TL;DR**: check the log file\n* This app has no UI, therefore all error output (exceptions and explicit logs) is written to [Nextcloud's log](https://docs.nextcloud.com/server/latest/admin_manual/configuration_server/logging_configuration.html),\nby default  */var/www/nextcloud/data/nextcloud.log* or */var/log/syslog*. Log level 3 is sufficient for all non-debug output.\n* There are no semantic checks for the SQL queries. As soon as a query string\n  is not empty the app assumes that it is a query and executes it. It's likely that you will\n  have typos in your SQL queries. Check the log to find out if and why SQL queries fail.\n* **Pro Tip**: use *jq* to parse and format Nextcloud's JSON logfile\n  * if not installed: `apt install jq`\n  * watch logfile starting at the bottom:\n\n    ```bash\n    jq -C 'select (.app==\"user_backend_sql_raw\")' /var/www/nextcloud/data/nextcloud.log  | less -R +G\n    ```\n\n    * `-C` enables colored output, later for *less* `-R` keeps it\n    * the `select` defines a filter to only show entries where the key `app` is set to this app's name\n    * `+G` jumps to end of file\n    * *less* does not auto-update, you need to quit using \u003ckbd\u003eq\u003c/kbd\u003e and start again\n* This app also logs non-SQL configuration errors, e.g. missing db name.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpancakeconnaisseur%2Fuser_backend_sql_raw","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpancakeconnaisseur%2Fuser_backend_sql_raw","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpancakeconnaisseur%2Fuser_backend_sql_raw/lists"}