From 346be9b1d0282a58cbf60f12e9ee7fcbcbf57c27 Mon Sep 17 00:00:00 2001 From: Larry Aasen Date: Wed, 31 Jul 2013 22:23:09 -0400 Subject: [PATCH] ## Version 0.2.4 * Closed Issue #7 - Option to use a different ActiveRecord class than RestClient * This update involves a lot of changes to a lot of files, but the functionality remains the same. The change was to rename RestClient to RestAppClient and rest_client to rest_client. This avoids a conflict with the popular rest-client gem that uses the same RestClient class name. --- CHANGELOG.md | 8 +++ README.md | 6 +-- .../install/install_generator.rb | 8 +-- ...st_client.rb => create_rest_app_client.rb} | 4 +- .../{rest_client.rb => rest_app_client.rb} | 2 +- lib/restful_api_authentication.rb | 2 +- lib/restful_api_authentication/checker.rb | 6 +-- lib/restful_api_authentication/version.rb | 2 +- .../{rest_client.rb => rest_app_client.rb} | 2 +- ... 20120423195432_create_rest_app_client.rb} | 4 +- test/dummy/db/schema.rb | 2 +- .../dummy/features/api/authentication.feature | 16 +++--- .../step_definitions/api_base_steps.rb | 14 +++--- test/dummy/features/step_definitions/hooks.rb | 4 +- .../{rest_clients.rb => rest_app_clients.rb} | 8 +-- .../dummy/spec/models/rest_app_client_spec.rb | 49 +++++++++++++++++++ test/dummy/spec/models/rest_client_spec.rb | 49 ------------------- 17 files changed, 97 insertions(+), 89 deletions(-) rename lib/generators/restful_api_authentication/install/templates/{create_rest_client.rb => create_rest_app_client.rb} (64%) rename lib/generators/restful_api_authentication/install/templates/{rest_client.rb => rest_app_client.rb} (95%) rename test/dummy/app/models/{rest_client.rb => rest_app_client.rb} (95%) rename test/dummy/db/migrate/{20120423195432_create_rest_client.rb => 20120423195432_create_rest_app_client.rb} (64%) rename test/dummy/spec/factories/{rest_clients.rb => rest_app_clients.rb} (61%) create mode 100644 test/dummy/spec/models/rest_app_client_spec.rb delete mode 100644 test/dummy/spec/models/rest_client_spec.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ff4df4..f6872e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Change History / Release Notes +## Version 0.2.4 +* Closed Issue #7 - Option to use a different ActiveRecord class than RestClient +* This update involves a lot of changes to a lot of files, but the functionality remains the same. The change was to rename RestClient to RestAppClient and rest_client to rest_client. This avoids a conflict with the popular rest-client gem that uses the same RestClient class name. + +## Version 0.2.3 +* Update checker.rb to automatically lowercase hashes for crypto lib compatibility +* Closed Issue #6 - Wrong request_uri in README's client example + ## Version 0.2.2 * Closed Issue #5 - Improperly formatted timestamps result in an uncaught exception diff --git a/README.md b/README.md index cfca956..069e066 100644 --- a/README.md +++ b/README.md @@ -43,10 +43,10 @@ Update the configuration (if you like) by editing the `config/restful_api_authen ### How It Works From A Client's Perspective -Before anyone can use a resource which is protected using this gem, that person/app must have a valid API key and secret. These are generated and stored as a RestClient model in your app. The easiest way to generate this is to use the Rails console: +Before anyone can use a resource which is protected using this gem, that person/app must have a valid API key and secret. These are generated and stored as a RestAppClient model in your app. The easiest way to generate this is to use the Rails console: ```ruby -new_app = RestClient.create(:name => "My New App", :description => "This is my new application that will access my RESTful API.") +new_app = RestAppClient.create(:name => "My New App", :description => "This is my new application that will access my RESTful API.") new_app.api_key new_app.secret ``` @@ -123,7 +123,7 @@ If the headers are not provided or the application fails to authenticate, your w ### Master Authentication -Some web services might require an extra bit of security (creating new RestClients or managing User records). In these cases, you can require "master" authorization. Then, any RestClient with the is_master attribute set to true can use the resources but the others cannot. +Some web services might require an extra bit of security (creating new RestAppClients or managing User records). In these cases, you can require "master" authorization. Then, any RestAppClient with the is_master attribute set to true can use the resources but the others cannot. Assuming you have authentication setup in your application controller, in the controller that requires master authentication: diff --git a/lib/generators/restful_api_authentication/install/install_generator.rb b/lib/generators/restful_api_authentication/install/install_generator.rb index 1a53585..ce24cd6 100644 --- a/lib/generators/restful_api_authentication/install/install_generator.rb +++ b/lib/generators/restful_api_authentication/install/install_generator.rb @@ -28,7 +28,7 @@ module Generators class InstallGenerator < ::Rails::Generators::Base include Rails::Generators::Migration source_root File.expand_path('../templates', __FILE__) - desc "This generator installs a restful_api_authentication.yml file, creates a RestClient model, and generates migrations for the RestfulApiAuthentication gem." + desc "This generator installs a restful_api_authentication.yml file, creates a RestAppClient model, and generates migrations for the RestfulApiAuthentication gem." def self.next_migration_number(path) unless @prev_migration_nr @@ -40,15 +40,15 @@ def self.next_migration_number(path) end def copy_migrations - migration_template "create_rest_client.rb", "db/migrate/create_rest_client.rb" + migration_template "create_rest_app_client.rb", "db/migrate/create_rest_app_client.rb" end def copy_the_config_file copy_file "restful_api_authentication.yml", "config/restful_api_authentication.yml" end - def copy_the_rest_client_model - copy_file "rest_client.rb", "app/models/rest_client.rb" + def copy_the_rest_app_client_model + copy_file "rest_app_client.rb", "app/models/rest_app_client.rb" end end diff --git a/lib/generators/restful_api_authentication/install/templates/create_rest_client.rb b/lib/generators/restful_api_authentication/install/templates/create_rest_app_client.rb similarity index 64% rename from lib/generators/restful_api_authentication/install/templates/create_rest_client.rb rename to lib/generators/restful_api_authentication/install/templates/create_rest_app_client.rb index 1c77215..90dd3b6 100644 --- a/lib/generators/restful_api_authentication/install/templates/create_rest_client.rb +++ b/lib/generators/restful_api_authentication/install/templates/create_rest_app_client.rb @@ -1,6 +1,6 @@ -class CreateRestClient < ActiveRecord::Migration +class CreateRestAppClient < ActiveRecord::Migration def change - create_table :rest_clients do |t| + create_table :rest_app_clients do |t| t.string :name t.text :description t.string :api_key diff --git a/lib/generators/restful_api_authentication/install/templates/rest_client.rb b/lib/generators/restful_api_authentication/install/templates/rest_app_client.rb similarity index 95% rename from lib/generators/restful_api_authentication/install/templates/rest_client.rb rename to lib/generators/restful_api_authentication/install/templates/rest_app_client.rb index 81c5792..cd2ac19 100644 --- a/lib/generators/restful_api_authentication/install/templates/rest_client.rb +++ b/lib/generators/restful_api_authentication/install/templates/rest_app_client.rb @@ -1,4 +1,4 @@ -class RestClient < ActiveRecord::Base +class RestAppClient < ActiveRecord::Base validates :name, :presence => true validates :description, :presence => true diff --git a/lib/restful_api_authentication.rb b/lib/restful_api_authentication.rb index fb5534a..ed77701 100644 --- a/lib/restful_api_authentication.rb +++ b/lib/restful_api_authentication.rb @@ -49,7 +49,7 @@ def authenticated? # This method should be used as a Rails before_filter in any controller in which one wants to ensure requests have valid client authentication headers and are considered master applications. # - # In order to be authenticated, not only do the headers need to be valid but the is_master flag must be true in the associated RestClient model. + # In order to be authenticated, not only do the headers need to be valid but the is_master flag must be true in the associated RestAppClient model. # # Master accounts can be used for anything you like but are typically reserved for admin specific requests that should only be performed by a limited number of clients. def authenticated_master? diff --git a/lib/restful_api_authentication/checker.rb b/lib/restful_api_authentication/checker.rb index bbc0f37..f7f8b6a 100644 --- a/lib/restful_api_authentication/checker.rb +++ b/lib/restful_api_authentication/checker.rb @@ -66,9 +66,9 @@ def authorized?(options = {}) private - # determines if a RestClient has master privileges or not + # determines if a RestAppClient has master privileges or not def is_master? - client = RestClient.where(:api_key => @http_headers[@@header_api_key]).first + client = RestAppClient.where(:api_key => @http_headers[@@header_api_key]).first client.is_master end @@ -93,7 +93,7 @@ def headers_have_values? # generates the string that is hashed to produce the signature def str_to_hash - client = RestClient.where(:api_key => @http_headers[@@header_api_key]).first + client = RestAppClient.where(:api_key => @http_headers[@@header_api_key]).first if client.nil? @errors << "client is not registered" end diff --git a/lib/restful_api_authentication/version.rb b/lib/restful_api_authentication/version.rb index 4b59bd7..8dce9c5 100644 --- a/lib/restful_api_authentication/version.rb +++ b/lib/restful_api_authentication/version.rb @@ -22,5 +22,5 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. module RestfulApiAuthentication - VERSION = "0.2.3" + VERSION = "0.2.4" end \ No newline at end of file diff --git a/test/dummy/app/models/rest_client.rb b/test/dummy/app/models/rest_app_client.rb similarity index 95% rename from test/dummy/app/models/rest_client.rb rename to test/dummy/app/models/rest_app_client.rb index 81c5792..cd2ac19 100644 --- a/test/dummy/app/models/rest_client.rb +++ b/test/dummy/app/models/rest_app_client.rb @@ -1,4 +1,4 @@ -class RestClient < ActiveRecord::Base +class RestAppClient < ActiveRecord::Base validates :name, :presence => true validates :description, :presence => true diff --git a/test/dummy/db/migrate/20120423195432_create_rest_client.rb b/test/dummy/db/migrate/20120423195432_create_rest_app_client.rb similarity index 64% rename from test/dummy/db/migrate/20120423195432_create_rest_client.rb rename to test/dummy/db/migrate/20120423195432_create_rest_app_client.rb index 1c77215..90dd3b6 100644 --- a/test/dummy/db/migrate/20120423195432_create_rest_client.rb +++ b/test/dummy/db/migrate/20120423195432_create_rest_app_client.rb @@ -1,6 +1,6 @@ -class CreateRestClient < ActiveRecord::Migration +class CreateRestAppClient < ActiveRecord::Migration def change - create_table :rest_clients do |t| + create_table :rest_app_clients do |t| t.string :name t.text :description t.string :api_key diff --git a/test/dummy/db/schema.rb b/test/dummy/db/schema.rb index c59c420..f087920 100644 --- a/test/dummy/db/schema.rb +++ b/test/dummy/db/schema.rb @@ -13,7 +13,7 @@ ActiveRecord::Schema.define(:version => 20120423195432) do - create_table "rest_clients", :force => true do |t| + create_table "rest_app_clients", :force => true do |t| t.string "name" t.text "description" t.string "api_key" diff --git a/test/dummy/features/api/authentication.feature b/test/dummy/features/api/authentication.feature index 47355f7..53ef24e 100644 --- a/test/dummy/features/api/authentication.feature +++ b/test/dummy/features/api/authentication.feature @@ -2,7 +2,7 @@ Feature: Authentication Testing The web service provides a way to test authentication. Scenario: When I post a request as JSON that is outside the default 4 minute window but within posted 10 minute window, then the app will say I am authorized. - Given a "rest_client" exists + Given a "rest_app_client" exists When I perform an authentication test with "outside default 4 minute window" Then the HTTP status code should be "200" And the response at index 0 of the JSON response data should be "authorized" @@ -26,43 +26,43 @@ Feature: Authentication Testing And the response at index 0 of the JSON response data should be "authorized" Scenario: When I post a request as HTTP with valid authentication credentials and a capitalized SHA hash, then the app will say I am authorized. - Given a "rest_client" exists + Given a "rest_app_client" exists When I perform an authentication test with "capitalized hash" Then the HTTP status code should be "200" And the response at index 0 of the JSON response data should be "authorized" Scenario: When I post a request with a timestamp that is too far in the past, then the app will say I am not authorized. - Given a "rest_client" exists + Given a "rest_app_client" exists When I perform an authentication test with "old authentication data" Then the HTTP status code should be "401" And the response at index 0 of the JSON response data should be "request is outside the required time window of 10 minutes" Scenario: When I post a request with a timestamp that is too far in the future, then the app will say I am not authorized. - Given a "rest_client" exists + Given a "rest_app_client" exists When I perform an authentication test with "future authentication data" Then the HTTP status code should be "401" And the response at index 0 of the JSON response data should be "request is outside the required time window of 10 minutes" Scenario: When I post a request with an unknown api key, then the app will say I am not authorized. - Given a "rest_client" exists + Given a "rest_app_client" exists When I perform an authentication test with "unknown api key data" Then the HTTP status code should be "401" And the response at index 0 of the JSON response data should be "client is not registered" Scenario: When I post a request with an improperly formatted timestamp, then the app will say I am not authorized. - Given a "rest_client" exists + Given a "rest_app_client" exists When I perform an authentication test with "improperly formatted timestamp" Then the HTTP status code should be "401" And the response at index 0 of the JSON response data should be "timestamp was in an invalid format; should be YYYY-MM-DD HH:MM:SS UTC" Scenario: When I post a request with an invalid secret, then the app will say I am not authorized. - Given a "rest_client" exists + Given a "rest_app_client" exists When I perform an authentication test with "invalid secret data" Then the HTTP status code should be "401" And the response at index 0 of the JSON response data should be "signature is invalid" Scenario: When I post a request with an invalid request URI, then the app will say I am not authorized. - Given a "rest_client" exists + Given a "rest_app_client" exists When I perform an authentication test with "invalid request uri data" Then the HTTP status code should be "401" And the response at index 0 of the JSON response data should be "signature is invalid" diff --git a/test/dummy/features/step_definitions/api_base_steps.rb b/test/dummy/features/step_definitions/api_base_steps.rb index 5a4f218..649c4e9 100644 --- a/test/dummy/features/step_definitions/api_base_steps.rb +++ b/test/dummy/features/step_definitions/api_base_steps.rb @@ -1,13 +1,13 @@ World(Rack::Test::Methods) Given /^I am authenticated$/ do - @rest_client = FactoryGirl.build(:rest_client) - @rest_client.is_master = false - header 'x-api-key', @rest_client.api_key + @rest_app_client = FactoryGirl.build(:rest_app_client) + @rest_app_client.is_master = false + header 'x-api-key', @rest_app_client.api_key end Given /^I have master permissions$/ do - @rest_client.is_master = true + @rest_app_client.is_master = true end Given /^an? "([^"]*)" exists$/ do |factory_name| @@ -16,7 +16,7 @@ # used to test authentication system When /^I perform an authentication test with "([^"]*)"$/ do |payload_type| - client = FactoryGirl.build(:rest_client) + client = FactoryGirl.build(:rest_app_client) ts = Time.now.utc request_uri = '/help/authentication' if payload_type == "old authentication data" @@ -46,7 +46,7 @@ ## Methods with parameters When /^I perform a ([^"]*) to "([^\"]*)" with an? "([^\"]*)" as ([^"]*)$/ do |method, path, factory_name, format| - @rest_client.save unless @rest_client.nil? + @rest_app_client.save unless @rest_app_client.nil? replace_attributes path, factory_name add_authentication_headers path params = nil @@ -75,7 +75,7 @@ ## Methods with parameters When /^I perform a GET to "([^\"]*)" as ([^"]*)$/ do |path, format| - @rest_client.save unless @rest_client.nil? + @rest_app_client.save unless @rest_app_client.nil? replace_attributes path, nil add_authentication_headers path params = nil diff --git a/test/dummy/features/step_definitions/hooks.rb b/test/dummy/features/step_definitions/hooks.rb index 9f078e9..88bf1b6 100644 --- a/test/dummy/features/step_definitions/hooks.rb +++ b/test/dummy/features/step_definitions/hooks.rb @@ -1,8 +1,8 @@ def add_authentication_headers(path) - unless @rest_client.nil? + unless @rest_app_client.nil? time_stamp = Time.now.utc.to_s header 'x-timestamp', time_stamp - header 'x-signature', (Digest::SHA256.new << @rest_client.secret + path + time_stamp).to_s + header 'x-signature', (Digest::SHA256.new << @rest_app_client.secret + path + time_stamp).to_s end end diff --git a/test/dummy/spec/factories/rest_clients.rb b/test/dummy/spec/factories/rest_app_clients.rb similarity index 61% rename from test/dummy/spec/factories/rest_clients.rb rename to test/dummy/spec/factories/rest_app_clients.rb index aaa565d..fc55039 100644 --- a/test/dummy/spec/factories/rest_clients.rb +++ b/test/dummy/spec/factories/rest_app_clients.rb @@ -1,20 +1,20 @@ FactoryGirl.define do - factory :rest_client, :class => RestClient do + factory :rest_app_client, :class => RestAppClient do name Random.alphanumeric description Random.paragraphs(1) api_key "69704d90-4b77-012f-c334-68a86d3dfd02" secret "1e5483d9c6ddbe2f26eecf444ec7a976b2836ab17a209a0940f4dfdee1b3bc93" is_master true end - factory :alternative_rest_client, :class => RestClient do + factory :alternative_rest_app_client, :class => RestAppClient do name Random.alphanumeric description Random.paragraphs(1) end - factory :rest_client_without_name, :class => RestClient do + factory :rest_app_client_without_name, :class => RestAppClient do name nil description Random.paragraphs(1) end - factory :rest_client_without_description, :class => RestClient do + factory :rest_app_client_without_description, :class => RestAppClient do name Random.alphanumeric end end diff --git a/test/dummy/spec/models/rest_app_client_spec.rb b/test/dummy/spec/models/rest_app_client_spec.rb new file mode 100644 index 0000000..068922b --- /dev/null +++ b/test/dummy/spec/models/rest_app_client_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +describe RestAppClient do + + it "should be able to generate a new api key upon request" do + rest_app_client = RestAppClient.new + rest_app_client.gen_api_key + rest_app_client.api_key.should_not be_nil + end + + it "should be able to generate a new secret upon request" do + rest_app_client = RestAppClient.new + rest_app_client.gen_secret + rest_app_client.secret.should_not be_nil + end + + it "should save a valid client" do + RestAppClient.should have(0).records + rest_app_client = FactoryGirl.build :rest_app_client + rest_app_client.save + RestAppClient.should have(1).record + end + + it "should not allow an empty name on create" do + rest_app_client = FactoryGirl.build :rest_app_client + rest_app_client.name = nil + rest_app_client.should_not be_valid + end + + it "should not allow an empty description on create" do + rest_app_client = FactoryGirl.build :rest_app_client + rest_app_client.description = nil + rest_app_client.should_not be_valid + end + + it "should enforce unique api keys" do + rest_app_client1 = FactoryGirl.build :rest_app_client + rest_app_client1.save + rest_app_client2 = FactoryGirl.build :rest_app_client + rest_app_client2.should_not be_valid + end + + it "should set is_master to false if not explicitly set" do + rest_app_client = RestAppClient.new(:name => "Test", :description => "Test") + rest_app_client.should be_valid + rest_app_client.is_master.should == false + end + +end diff --git a/test/dummy/spec/models/rest_client_spec.rb b/test/dummy/spec/models/rest_client_spec.rb deleted file mode 100644 index 0398cc2..0000000 --- a/test/dummy/spec/models/rest_client_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -require 'spec_helper' - -describe RestClient do - - it "should be able to generate a new api key upon request" do - rest_client = RestClient.new - rest_client.gen_api_key - rest_client.api_key.should_not be_nil - end - - it "should be able to generate a new secret upon request" do - rest_client = RestClient.new - rest_client.gen_secret - rest_client.secret.should_not be_nil - end - - it "should save a valid client" do - RestClient.should have(0).records - rest_client = FactoryGirl.build :rest_client - rest_client.save - RestClient.should have(1).record - end - - it "should not allow an empty name on create" do - rest_client = FactoryGirl.build :rest_client - rest_client.name = nil - rest_client.should_not be_valid - end - - it "should not allow an empty description on create" do - rest_client = FactoryGirl.build :rest_client - rest_client.description = nil - rest_client.should_not be_valid - end - - it "should enforce unique api keys" do - rest_client1 = FactoryGirl.build :rest_client - rest_client1.save - rest_client2 = FactoryGirl.build :rest_client - rest_client2.should_not be_valid - end - - it "should set is_master to false if not explicitly set" do - rest_client = RestClient.new(:name => "Test", :description => "Test") - rest_client.should be_valid - rest_client.is_master.should == false - end - -end