Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ActiveRecord suppport authentication with non-postgres database #51547

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

davispuh
Copy link

@davispuh davispuh commented Apr 11, 2024

Motivation / Background

I'm using PostgreSQL and I don't allow any user to connect to it using postgres database. Instead all users need to connect using their own database.

Currently Rails applications don't work with such configuration because of hardcoded postgres database that can't be changed. That is I set database in config/database.yml but Rails will still attempt to connect using postgres database.

Detail

Use database from config/database.yml instead of hardcoded postgres .

Note that there is one issue with this PR - what if the specified database in config/database.yml doesn't exist?
I see 2 ways to solve that:

  • ask user to create it manually, it's very trivial just execute createdb $NAME which is provided by PostgreSQL.
  • in case we fail, try again using postgres database but note that even then our user might not have CREATEDB permissions.

Additional information

Which users are allowed to connect with which databases can be configured in pg_hba.conf.

For example config like:

local    rails    rails   peer

Means that rails user is allowed to connect using Unix domain socket with rails DB user using rails database. So connecting using postgres database will be denied.
Note that this doesn't affect database permissions and rails user might be allowed to query and modify postgres database even while being connected using rails database.

It took me a while to hunt down this issue but basically application I was trying to setup (rails db:setup) was failing with:

$ rails db:setup --trace
** Invoke db:setup (first_time)
** Invoke db:create (first_time)
** Invoke db:load_config (first_time)
** Invoke environment (first_time)
** Execute environment
** Execute db:load_config
** Execute db:create
We could not find your database: postgres. Which can be found in the database configuration file located at config/database.yml.

To resolve this issue:

- Did you create the database for this app, or delete it? You may need to create your database.
- Has the database name changed? Check your database.yml config has the correct database name.

To create your database, run:

        bin/rails db:create
Couldn't create 'rails' database. Please check your configuration.
rails aborted!
ActiveRecord::NoDatabaseError: We could not find your database: postgres. Which can be found in the database configuration file located at config/database.yml.

To resolve this issue:

- Did you create the database for this app, or delete it? You may need to create your database.
- Has the database name changed? Check your database.yml config has the correct database name.

To create your database, run:

        bin/rails db:create
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/postgresql_adapter.rb:81:in `rescue in new_client'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/postgresql_adapter.rb:77:in `new_client'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/postgresql_adapter.rb:37:in `postgresql_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:656:in `public_send'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:656:in `new_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:700:in `checkout_new_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:679:in `try_to_checkout_new_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:640:in `acquire_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:341:in `checkout'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:181:in `connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_handler.rb:211:in `retrieve_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_handling.rb:313:in `retrieve_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_handling.rb:280:in `connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/postgresql_database_tasks.rb:12:in `connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/postgresql_database_tasks.rb:26:in `create'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:122:in `create'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:184:in `block in create_current'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:557:in `block (2 levels) in each_current_configuration'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:554:in `each'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:554:in `block in each_current_configuration'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:553:in `each'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:553:in `each_current_configuration'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:184:in `create_current'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/railties/databases.rake:45:in `block (2 levels) in <main>'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `block in execute'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `each'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `execute'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:219:in `block in invoke_with_call_chain'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `synchronize'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:243:in `block in invoke_prerequisites'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:241:in `each'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:241:in `invoke_prerequisites'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:218:in `block in invoke_with_call_chain'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `synchronize'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:188:in `invoke'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:160:in `invoke_task'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block (2 levels) in top_level'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `each'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block in top_level'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:125:in `run_with_threads'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:110:in `top_level'
/usr/lib/ruby/gems/3.0.0/gems/railties-7.0.8.1/lib/rails/commands/rake/rake_command.rb:24:in `block (2 levels) in perform'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:186:in `standard_exception_handling'
/usr/lib/ruby/gems/3.0.0/gems/railties-7.0.8.1/lib/rails/commands/rake/rake_command.rb:24:in `block in perform'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/rake_module.rb:59:in `with_application'
/usr/lib/ruby/gems/3.0.0/gems/railties-7.0.8.1/lib/rails/commands/rake/rake_command.rb:18:in `perform'
/usr/lib/ruby/gems/3.0.0/gems/railties-7.0.8.1/lib/rails/command.rb:51:in `invoke'
/usr/lib/ruby/gems/3.0.0/gems/railties-7.0.8.1/lib/rails/commands.rb:18:in `<main>'
<internal:/usr/lib/ruby/site_ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
<internal:/usr/lib/ruby/site_ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
/usr/lib/ruby/gems/3.0.0/gems/bootsnap-1.16.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
bin/rails:4:in `<main>'

Caused by:
PG::ConnectionBad: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  pg_hba.conf rejects connection for host "[local]", user "rails", database "postgres", no encryption
/usr/lib/ruby/gems/3.0.0/gems/pg-1.5.5/lib/pg/connection.rb:696:in `async_connect_or_reset'
/usr/lib/ruby/gems/3.0.0/gems/pg-1.5.5/lib/pg/connection.rb:824:in `connect_to_hosts'
/usr/lib/ruby/gems/3.0.0/gems/pg-1.5.5/lib/pg/connection.rb:759:in `new'
/usr/lib/ruby/gems/3.0.0/gems/pg-1.5.5/lib/pg.rb:63:in `connect'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/postgresql_adapter.rb:78:in `new_client'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/postgresql_adapter.rb:37:in `postgresql_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:656:in `public_send'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:656:in `new_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:700:in `checkout_new_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:679:in `try_to_checkout_new_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:640:in `acquire_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:341:in `checkout'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:181:in `connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_adapters/abstract/connection_handler.rb:211:in `retrieve_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_handling.rb:313:in `retrieve_connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/connection_handling.rb:280:in `connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/postgresql_database_tasks.rb:12:in `connection'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/postgresql_database_tasks.rb:26:in `create'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:122:in `create'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:184:in `block in create_current'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:557:in `block (2 levels) in each_current_configuration'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:554:in `each'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:554:in `block in each_current_configuration'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:553:in `each'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:553:in `each_current_configuration'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/tasks/database_tasks.rb:184:in `create_current'
/usr/lib/ruby/gems/3.0.0/gems/activerecord-7.0.8.1/lib/active_record/railties/databases.rake:45:in `block (2 levels) in <main>'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `block in execute'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `each'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:281:in `execute'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:219:in `block in invoke_with_call_chain'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `synchronize'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:243:in `block in invoke_prerequisites'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:241:in `each'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:241:in `invoke_prerequisites'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:218:in `block in invoke_with_call_chain'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `synchronize'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:199:in `invoke_with_call_chain'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/task.rb:188:in `invoke'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:160:in `invoke_task'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block (2 levels) in top_level'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `each'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:116:in `block in top_level'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:125:in `run_with_threads'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:110:in `top_level'
/usr/lib/ruby/gems/3.0.0/gems/railties-7.0.8.1/lib/rails/commands/rake/rake_command.rb:24:in `block (2 levels) in perform'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/application.rb:186:in `standard_exception_handling'
/usr/lib/ruby/gems/3.0.0/gems/railties-7.0.8.1/lib/rails/commands/rake/rake_command.rb:24:in `block in perform'
/usr/lib/ruby/gems/3.0.0/gems/rake-13.0.6/lib/rake/rake_module.rb:59:in `with_application'
/usr/lib/ruby/gems/3.0.0/gems/railties-7.0.8.1/lib/rails/commands/rake/rake_command.rb:18:in `perform'
/usr/lib/ruby/gems/3.0.0/gems/railties-7.0.8.1/lib/rails/command.rb:51:in `invoke'
/usr/lib/ruby/gems/3.0.0/gems/railties-7.0.8.1/lib/rails/commands.rb:18:in `<main>'
<internal:/usr/lib/ruby/site_ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
<internal:/usr/lib/ruby/site_ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:37:in `require'
/usr/lib/ruby/gems/3.0.0/gems/bootsnap-1.16.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
bin/rails:4:in `<main>'
Tasks: TOP => db:setup => db:create

Notice the very misleading messages:

We could not find your database: postgres. Which can be found in the database configuration file located at config/database.yml.
Couldn't create 'rails' database. Please check your configuration.
...
ActiveRecord::NoDatabaseError: We could not find your database: postgres. Which can be found in the database configuration file located at config/database.yml.

While in config/database.yml I have database: rails - really a hair pulling moment 🤯

And also you can see there

PG::ConnectionBad: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: FATAL:  pg_hba.conf rejects connection for host "[local]", user "rails", database "postgres", no encryption

Checklist

Before submitting the PR make sure the following are checked:

  • This Pull Request is related to one change. Unrelated changes should be opened in separate PRs.
  • Commit message has a detailed description of what changed and why. If this PR fixes a related issue include it in the commit message. Ex: [Fix #issue-number]
  • Tests are added or updated if you fix a bug or add a feature.
  • CHANGELOG files are updated for the changed libraries if there is a behavior change or additional feature. Minor bug fixes and documentation changes should not be included.

It's possible to configure PostgreSQL user in a way that doesn't
allow it to connect using "postgres" database but instead only
his own database. For more info see `pg_hba.conf`

Currently such users wouldn't be able to be used with ActiveRecord
so fix that by allowing this.

Note that permission to connect and actual permission to query "postgres"
are separate and independent things.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

1 participant