Skip to content

Commit

Permalink
Add generator to set up HTTP Basic authentication easily
Browse files Browse the repository at this point in the history
This asks for the username, generates and password and stores both in
Rails credentials.
  • Loading branch information
rosa committed Dec 2, 2024
1 parent 4dbf868 commit 31445bb
Show file tree
Hide file tree
Showing 8 changed files with 95 additions and 22 deletions.
34 changes: 18 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,31 +56,29 @@ RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile

*Note: Legacy CSS bundlers `sass-rails` and `sassc-rails` may fail to compile some of the CSS vendored into this library from [Bulma](https://github.com/jgthms/bulma), which was created in [Dart SASS](https://sass-lang.com/dart-sass/). You will therefore need to upgrade to `dartsass-rails` or some library that relies on it, like `cssbundling-rails`.*

### Authentication and base controller class
### Authentication

By default, Mission Control's controllers will extend the host app's `ApplicationController`. If no authentication is enforced, `/jobs` will be available to everyone.

#### HTTP authentication
Mission Control comes with **HTTP basic authentication enabled and closed** by default. Credentials are stored in [Rails's credentials](https://edgeguides.rubyonrails.org/security.html#custom-credentials) like this:
```yml
mission_control:
http_basic_auth_user: dev
http_basic_auth_password: secret
```
You can set a simple HTTP authentication by either using
If no credentials are configured, Mission Control won't be accessible. To set these up, you can run the generator provided like this:
```ruby
Rails.application.configure do
config.mission_control.jobs.http_auth_user = "captain"
config.mission_control.jobs.http_auth_password = "topsecret"
end
```
or you can set it via ENV
bin/rails mission_control:jobs:authentication:init
```

```shell
ENV["MISSION_CONTROL_JOBS_HTTP_AUTH_USER"]=captain
ENV["MISSION_CONTROL_JOBS_HTTP_AUTH_password"]=topsecret
To set them up for different environments you can use the `RAILS_ENV` environment variable, like this:
```
RAILS_ENV=production bin/rails mission_control:jobs:authentication:init
```
If no value is provided (`nil`, `""` or `false`), authentication is skipped.

#### Custom authentication

You might want to implement your own authentication. To make this easier, you can specify a different controller as the base class for Mission Control's controllers:
You can provide your own authentication mechanism, for example, if you have a certain type of admin user in your app that can access Mission Control. To make this easier, you can specify a different controller as the base class for Mission Control's controllers. By default, Mission Control's controllers will extend the host app's `ApplicationController`, but you can change this easily:

```ruby
Rails.application.configure do
Expand All @@ -91,7 +89,11 @@ end
Or, in your environment config or `application.rb`:
```ruby
config.mission_control.jobs.base_controller_class = "AdminController"
```

If you do this, you can disable the default HTTP Basic Authentication using the following option:
```ruby
config.mission_control.jobs.http_basic_auth_enabled = false
```

### Other configuration settings
Expand Down
59 changes: 59 additions & 0 deletions lib/generators/mission_control/jobs/authentication_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
class MissionControl::Jobs::AuthenticationGenerator < Rails::Generators::Base
def init
if credentials_accessible?
if authentication_configured?
say "HTTP Basic Authentication is already configured for `#{Rails.env}`. You can edit it using `credentials:edit`"
else
say "Setting up credentials for HTTP Basic Authentication for `#{Rails.env}` environment."
say ""

username = ask "Enter username: "
password = SecureRandom.base58(64)

store_credentials(username, password)
say "Username and password stored in Rails encrypted credentials."
say ""
say "You can now access Mission Control – Jobs with: "
say ""
say " - Username: #{username}"
say " - password: #{password}"
say ""
say "You can also edit these in the future via `credentials:edit`"
end
else
say "Rails credentials haven't been configured or aren't accessible. Configure them following the instructions in `credentials:help`"
end
end

private
def credentials_accessible?
credentials.read.present?
end

def authentication_configured?
%i[ http_basic_auth_user http_basic_auth_password ].any? do |key|
credentials.dig(:mission_control, key).present?
end
end

def store_credentials(username, password)
content = credentials.read + "\n" + http_authentication_entry(username, password) + "\n"
credentials.write(content)
end

def credentials
@credentials ||= Rails.application.encrypted(config.content_path, key_path: config.key_path)
end

def config
Rails.application.config.credentials
end

def http_authentication_entry(username, password)
<<~ENTRY
mission_control:
http_basic_auth_user: #{username}
http_basic_auth_password: #{password}
ENTRY
end
end
2 changes: 2 additions & 0 deletions lib/mission_control/jobs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
loader.push_dir(File.expand_path("..", __dir__))
loader.ignore("#{File.expand_path("..", __dir__)}/resque")
loader.ignore("#{File.expand_path("..", __dir__)}/mission_control/jobs/tasks.rb")
loader.ignore("#{File.expand_path("..", __dir__)}/generators")
loader.setup

module MissionControl
Expand Down
8 changes: 6 additions & 2 deletions lib/mission_control/jobs/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ module Jobs
class Engine < ::Rails::Engine
isolate_namespace MissionControl::Jobs

rake_tasks do
load "mission_control/jobs/tasks.rb"
end

initializer "mission_control-jobs.middleware" do |app|
if app.config.api_only
config.middleware.use ActionDispatch::Flash
Expand All @@ -31,8 +35,8 @@ class Engine < ::Rails::Engine
end

initializer "mission_control-jobs.http_basic_auth" do |app|
config.mission_control.jobs.http_basic_auth_user = app.credentials.dig(:mission_control, :http_basic_auth_user),
config.mission_control.jobs.http_basic_auth_password = app.credentials.dig(:mission_control, :http_basic_auth_password)
MissionControl::Jobs.http_basic_auth_user = app.credentials.dig(:mission_control, :http_basic_auth_user)
MissionControl::Jobs.http_basic_auth_password = app.credentials.dig(:mission_control, :http_basic_auth_password)
end

initializer "mission_control-jobs.active_job.extensions" do
Expand Down
8 changes: 8 additions & 0 deletions lib/mission_control/jobs/tasks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace :mission_control do
namespace :jobs do
desc "Configure HTTP Basic Authentication"
task "authentication:init" do
Rails::Command.invoke :generate, [ "mission_control:jobs:authentication" ]
end
end
end
4 changes: 0 additions & 4 deletions lib/tasks/mission_control/jobs_tasks.rake

This file was deleted.

1 change: 1 addition & 0 deletions test/dummy/config/credentials/development.key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
67f819f011ec672273c91cf789afb5d7
1 change: 1 addition & 0 deletions test/dummy/config/credentials/development.yml.enc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3wr+OnlAdcQJl0WURd7JXv+pleXbJVWozLH4JfPU6dGc9A0VlQ/kQosdPqDF7Yf/WrLtodre258ALf0ZHE2bQYgH3Eq0cJQ7xN8WwfGjBjXiL6uWaOHcfgcPVNg4E3Ag+YN3EOH8aquSttX7Uqyfv3tPlYQBQ7fs8lXjx3APfl3P8Vk2Yz6bhQcBgXhtFqH+--f7tDKb8EHxaT9l+Z--WIHpj/e3mEcqupnMrf5fvw==

0 comments on commit 31445bb

Please sign in to comment.