Skip to content

Commit

Permalink
## Version 0.2.4
Browse files Browse the repository at this point in the history
* Closed Issue davesloan#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.
  • Loading branch information
larryaasen committed Aug 1, 2013
1 parent 1ebfead commit 346be9b
Show file tree
Hide file tree
Showing 17 changed files with 97 additions and 89 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down Expand Up @@ -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:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class RestClient < ActiveRecord::Base
class RestAppClient < ActiveRecord::Base

validates :name, :presence => true
validates :description, :presence => true
Expand Down
2 changes: 1 addition & 1 deletion lib/restful_api_authentication.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down
6 changes: 3 additions & 3 deletions lib/restful_api_authentication/checker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/restful_api_authentication/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
class RestClient < ActiveRecord::Base
class RestAppClient < ActiveRecord::Base

validates :name, :presence => true
validates :description, :presence => true
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 1 addition & 1 deletion test/dummy/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
16 changes: 8 additions & 8 deletions test/dummy/features/api/authentication.feature
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
14 changes: 7 additions & 7 deletions test/dummy/features/step_definitions/api_base_steps.rb
Original file line number Diff line number Diff line change
@@ -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|
Expand All @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions test/dummy/features/step_definitions/hooks.rb
Original file line number Diff line number Diff line change
@@ -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

Expand Down
Original file line number Diff line number Diff line change
@@ -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
49 changes: 49 additions & 0 deletions test/dummy/spec/models/rest_app_client_spec.rb
Original file line number Diff line number Diff line change
@@ -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
49 changes: 0 additions & 49 deletions test/dummy/spec/models/rest_client_spec.rb

This file was deleted.

0 comments on commit 346be9b

Please sign in to comment.