From c98a18af046c84395b69135bcc07cadef367d445 Mon Sep 17 00:00:00 2001 From: Benjamin Ortiz Date: Mon, 17 Jun 2024 09:31:44 -0700 Subject: [PATCH] Separate API Key auth support generation (#124) ## Problem The generator `stitches:api` forces adding in API Key auth support. ## Solution Introduce a new generator `stitches:api_migration` that can add in API Key auth support. --- README.md | 15 ++++++++++-- lib/stitches/api_generator.rb | 12 ---------- lib/stitches/api_migration_generator.rb | 23 +++++++++++++++++++ .../config/initializers/stitches.rb | 4 ++++ .../spec/acceptance/ping_v1_spec.rb | 6 +++-- .../spec/features/api_spec.rb.erb | 3 +++ lib/stitches/spec/test_headers.rb | 2 +- lib/stitches_norailtie.rb | 1 + spec/integration/add_to_rails_app_spec.rb | 1 + 9 files changed, 50 insertions(+), 17 deletions(-) create mode 100644 lib/stitches/api_migration_generator.rb diff --git a/README.md b/README.md index fdeebf8..4f4bd89 100644 --- a/README.md +++ b/README.md @@ -26,9 +26,20 @@ bundle install Then, set it up: -``` +```bash > bin/rails generate stitches:api -> bundle exec rake db:migrate +> bin/rails generate stitches:api_migration # only if you're using API key authentication +> bundle exec rake db:migrate # only if you're using API key authentication +``` + +### Disable API Key Support + +If you're not using the API Key authentication feature of the library, configure stitches: + +```ruby +Stitches.configure do |config| + config.disable_api_key_support = true +end ``` ### Upgrading from an older version diff --git a/lib/stitches/api_generator.rb b/lib/stitches/api_generator.rb index d3f7733..aaa6a8c 100644 --- a/lib/stitches/api_generator.rb +++ b/lib/stitches/api_generator.rb @@ -2,14 +2,8 @@ module Stitches class ApiGenerator < Rails::Generators::Base - include Rails::Generators::Migration - source_root(File.expand_path(File.join(File.dirname(__FILE__), "generator_files"))) - def self.next_migration_number(path) - Time.now.utc.strftime("%Y%m%d%H%M%S") - end - desc "Bootstraps your API service with a basic ping controller and spec to ensure everything is setup properly" def bootstrap_api gem_group :development, :test do @@ -44,16 +38,10 @@ def bootstrap_api copy_file "app/controllers/api/v2.rb" copy_file "app/controllers/api/v1/pings_controller.rb" copy_file "app/controllers/api/v2/pings_controller.rb" - copy_file "app/models/api_client.rb" copy_file "config/initializers/stitches.rb" - copy_file "lib/tasks/generate_api_key.rake" template "spec/features/api_spec.rb.erb", "spec/features/api_spec.rb" copy_file "spec/acceptance/ping_v1_spec.rb", "spec/acceptance/ping_v1_spec.rb" - migration_template "db/migrate/enable_uuid_ossp_extension.rb", "db/migrate/enable_uuid_ossp_extension.rb" - sleep 1 # allow clock to tick so we get different numbers - migration_template "db/migrate/create_api_clients.rb", "db/migrate/create_api_clients.rb" - inject_into_file 'spec/rails_helper.rb', %q{ config.include RSpec::Rails::RequestExampleGroup, type: :feature }, before: /^end/ diff --git a/lib/stitches/api_migration_generator.rb b/lib/stitches/api_migration_generator.rb new file mode 100644 index 0000000..43282a0 --- /dev/null +++ b/lib/stitches/api_migration_generator.rb @@ -0,0 +1,23 @@ +require 'rails/generators' + +module Stitches + class ApiMigrationGenerator < Rails::Generators::Base + include Rails::Generators::Migration + + source_root(File.expand_path(File.join(File.dirname(__FILE__), "generator_files"))) + + def self.next_migration_number(path) + Time.now.utc.strftime("%Y%m%d%H%M%S") + end + + desc "Add a DB backed key storage system for your API service" + def bootstrap_api_migration + copy_file "app/models/api_client.rb" + copy_file "lib/tasks/generate_api_key.rake" + + migration_template "db/migrate/enable_uuid_ossp_extension.rb", "db/migrate/enable_uuid_ossp_extension.rb" + sleep 1 # allow clock to tick so we get different numbers + migration_template "db/migrate/create_api_clients.rb", "db/migrate/create_api_clients.rb" + end + end +end diff --git a/lib/stitches/generator_files/config/initializers/stitches.rb b/lib/stitches/generator_files/config/initializers/stitches.rb index f437ea3..92fa8e7 100644 --- a/lib/stitches/generator_files/config/initializers/stitches.rb +++ b/lib/stitches/generator_files/config/initializers/stitches.rb @@ -8,6 +8,10 @@ # but generally should be a string with no spaces or special characters. configuration.custom_http_auth_scheme = "CustomKeyAuth" + # Disable API Key feature. Enable it to add a database backed API Key auth scheme. + # Be sure to run `bin/rails generate stitches:api_migration` after enabling. + configuration.disable_api_key_support = true + # Env var that gets the primary key of the authenticated ApiKey # for access in your controllers, so they don't need to re-parse the header # configuration.env_var_to_hold_api_client_primary_key = "YOUR_ENV_VAR" diff --git a/lib/stitches/generator_files/spec/acceptance/ping_v1_spec.rb b/lib/stitches/generator_files/spec/acceptance/ping_v1_spec.rb index 4ce42f2..ea6f1b0 100644 --- a/lib/stitches/generator_files/spec/acceptance/ping_v1_spec.rb +++ b/lib/stitches/generator_files/spec/acceptance/ping_v1_spec.rb @@ -11,7 +11,8 @@ response_field :status, "The status of the ping", scope: "ping", "Type" => "String" example "ping the server to validate your client's happy path" do - header "Authorization", "CustomKeyAuth key=#{api_client.key}" + # Only needed if you're using API Key authentication + # header "Authorization", "CustomKeyAuth key=#{api_client.key}" do_request result = JSON.parse(response_body) @@ -33,7 +34,8 @@ example "ping the server to validate your client's error handling" do - header "Authorization", "CustomKeyAuth key=#{api_client.key}" + # Only needed if you're using API Key authentication + # header "Authorization", "CustomKeyAuth key=#{api_client.key}" do_request result = JSON.parse(response_body) diff --git a/lib/stitches/generator_files/spec/features/api_spec.rb.erb b/lib/stitches/generator_files/spec/features/api_spec.rb.erb index a5b2046..beba29a 100644 --- a/lib/stitches/generator_files/spec/features/api_spec.rb.erb +++ b/lib/stitches/generator_files/spec/features/api_spec.rb.erb @@ -48,6 +48,8 @@ feature "general API stuff" do expect(response).to have_api_error(code: "test", message: "OH NOES!") end +<%# Remove always false if statement to enable API Key authentication %> +<% if false %> scenario "no auth header given" do headers = TestHeaders.new(api_client: nil) <% if ::Rails::VERSION::MAJOR >= 5 -%> @@ -80,6 +82,7 @@ feature "general API stuff" do expect(response).to have_auth_error end +<% end %> scenario "no version" do headers = TestHeaders.new(version: nil) diff --git a/lib/stitches/spec/test_headers.rb b/lib/stitches/spec/test_headers.rb index 74dbbfe..dc503e5 100644 --- a/lib/stitches/spec/test_headers.rb +++ b/lib/stitches/spec/test_headers.rb @@ -6,7 +6,7 @@ def initialize(options={}) "Accept" => full_mimetype, "Content-Type" => full_mimetype, }.tap { |headers| - set_authorization_header(headers,options) + set_authorization_header(headers,options) unless Stitches.configuration.disable_api_key_support } end diff --git a/lib/stitches_norailtie.rb b/lib/stitches_norailtie.rb index b4891ed..ce7edd4 100644 --- a/lib/stitches_norailtie.rb +++ b/lib/stitches_norailtie.rb @@ -12,6 +12,7 @@ def self.configuration require 'stitches/error' require 'stitches/errors' require 'stitches/api_generator' +require 'stitches/api_migration_generator' require 'stitches/add_deprecation_generator' require 'stitches/add_enabled_to_api_clients_generator' require 'stitches/add_disabled_at_to_api_clients_generator' diff --git a/spec/integration/add_to_rails_app_spec.rb b/spec/integration/add_to_rails_app_spec.rb index 8f13e93..977d273 100644 --- a/spec/integration/add_to_rails_app_spec.rb +++ b/spec/integration/add_to_rails_app_spec.rb @@ -61,6 +61,7 @@ def run(command) it "works as described in the README" do run "bin/rails generate stitches:api" + run "bin/rails generate stitches:api_migration" # Yuck! So much duplication! BUT: Rails app templates have a notoriously silent failure mode, so mostly # what this is doing is ensuring that the generator inserted stuff when asked and that the very basics of what happens