Skip to content
This repository has been archived by the owner on Apr 9, 2020. It is now read-only.

Commit

Permalink
Merge pull request #18 from harrystech/add-base64-support
Browse files Browse the repository at this point in the history
JobsController: Accept base64 encoded payloads
  • Loading branch information
pjambet authored Jan 30, 2017
2 parents 8fc16d6 + e9eb268 commit 56deac2
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 36 deletions.
31 changes: 31 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,37 @@ You may optionally set a private RSA key to encrypt the `public_id` to uniquely

CRONUT_PRIVATE_KEY: -----BEGIN RSA PRIVATE KEY----- <private_key> -----END RSA PRIVATE KEY-----

Sending an encrypted payload from ruby is fairly straightforward, you can simply
encrypt the payload with :
`OpenSSL::PKey::RSA.new(public_key_string_coming_from_the_env_a_file_or_anywhere_else).public_encrypt(str)`
and attach this string to a POST request.

Here is an example with faraday:

```ruby
host = "https://your.cronut.host.com"
public_key = ENV["CRONUT_PUBLIC_KEY"] # Or from a file, the string should look like "-----BEGIN PUBLIC KEY-----\n<the key>\n-----END PUBLIC KEY-----"
conn = Faraday.new(host) do |c|
c.request :url_encoded
c.use Faraday::Adapter::NetHttp
c.headers = {
"X-CRONUT-API-TOKEN" => "<token>"
}
end

conn.post "/ping/", {:public_id => OpenSSL::PKey::RSA.new(public_key).public_encrypt(str)}
```

Another (simpler) approach is to use the [ping-me-maybe gem](https://github.com/harrystech/ping-me-maybe)

#### Base64 option

Ruby uses a non standard string representation of the underlying encrypted bytes
and this can be quite tricky to reproduce when trying to ping cronut from
another environment, such as the JVM.
In this can you can base 64 encode the encrypted bytes and use the `/v2/ping`
endpoint instead. It expects the payload to be base 64 encoded by default

Usage
-----
On Cronut dashboard, you can schedule two types of jobs: interval jobs and cron jobs.
Expand Down
10 changes: 9 additions & 1 deletion app/controllers/jobs_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ def ping
begin
str = params[:public_id]
if Encryptor.enabled?
if use_base64?
str = Base64.decode64(str)
end
str = Encryptor.decrypt(str)
end
array = str.split("-")
Expand All @@ -113,7 +116,8 @@ def ping
raise "Timestamp does not match"
end
@job = Job.find_by_public_id!(array[1])
rescue Exception => e
rescue StandardError => e
puts e.message
raise ActiveRecord::RecordNotFound.new('Not Found')
end

Expand All @@ -132,6 +136,10 @@ def verify_api_token

private

def use_base64?
params[:use_base64].to_s == "true"
end

def job_params
params.require(:job).permit(:name, :notifications, {notification_ids: []}, :frequency, :cron_expression, :buffer_time)
end
Expand Down
3 changes: 2 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
resources :notifications
resources :jobs

post 'ping/' => "jobs#ping"
post 'ping' => "jobs#ping"
post 'v2/ping' => "jobs#ping", defaults: {use_base64: true}

root to: 'jobs#index'
# The priority is based upon order of creation:
Expand Down
10 changes: 9 additions & 1 deletion spec/controllers/jobs_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@
end
end

describe "GET ping" do
describe "POST ping" do
before(:each) do
invalid_basic_auth_login
@job = IntervalJob.create!({:name => "Test IntervalJob", :frequency => 600})
Expand Down Expand Up @@ -299,6 +299,14 @@
response.status.should eq 200
@job.last_successful_time.should_not be_nil
end

it "pings with valid token and a base64 encoded payload" do
request.headers[JobsController::API_TOKEN_HEADER] = @token.token
post :ping, {:public_id => Base64.strict_encode64(Encryptor.encrypt(@str)), use_base64: "true"}, valid_session
@job.reload
response.status.should eq 200
@job.last_successful_time.should_not be_nil
end
end

end
86 changes: 53 additions & 33 deletions spec/requests/jobs_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@
ActiveRecord::Base.connection.reset_pk_sequence!('notifications')
end

describe "POST ping" do
context "ping" do

before(:each) do
#invalid_basic_auth_login
@job = IntervalJob.create!({:name => "Test IntervalJob", :frequency => 600})
Expand All @@ -57,42 +58,61 @@
@token.destroy
end

it "ignores pings with unencrypted public id" do
headers[JobsController::API_TOKEN_HEADER] = @token.token
expect {
post "/ping", {:public_id => @str}, headers
}.to raise_error(ActiveRecord::RecordNotFound)
@job.reload
@job.last_successful_time.should be_nil
end
describe "POST ping" do
it "ignores pings with unencrypted public id" do
headers[JobsController::API_TOKEN_HEADER] = @token.token
expect {
post "/ping", {:public_id => @str}, headers
}.to raise_error(ActiveRecord::RecordNotFound)
@job.reload
@job.last_successful_time.should be_nil
end

it "ignores pings with encrypted wrong id" do
wrong_str = "#{Time.now.to_i.to_s}-abc"
headers[JobsController::API_TOKEN_HEADER] = @token.token
expect {
post "/ping", {:public_id => Encryptor.encrypt(wrong_str)}, headers
}.to raise_error(ActiveRecord::RecordNotFound)
@job.reload
@job.last_successful_time.should be_nil
end
it "ignores pings with encrypted wrong id" do
wrong_str = "#{Time.now.to_i.to_s}-abc"
headers[JobsController::API_TOKEN_HEADER] = @token.token
expect {
post "/ping", {:public_id => Encryptor.encrypt(wrong_str)}, headers
}.to raise_error(ActiveRecord::RecordNotFound)
@job.reload
@job.last_successful_time.should be_nil
end

it "ignores pings with encrypted wrong date" do
wrong_str = "#{(Time.now - 31.seconds).to_i.to_s}-#{@job.public_id}"
headers[JobsController::API_TOKEN_HEADER] = @token.token
expect {
post "/ping", {:public_id => Encryptor.encrypt(wrong_str)}, headers
}.to raise_error(ActiveRecord::RecordNotFound)
@job.reload
@job.last_successful_time.should be_nil
it "ignores pings with encrypted wrong date" do
wrong_str = "#{(Time.now - 31.seconds).to_i.to_s}-#{@job.public_id}"
headers[JobsController::API_TOKEN_HEADER] = @token.token
expect {
post "/ping", {:public_id => Encryptor.encrypt(wrong_str)}, headers
}.to raise_error(ActiveRecord::RecordNotFound)
@job.reload
@job.last_successful_time.should be_nil
end

it "pings with valid token" do
headers[JobsController::API_TOKEN_HEADER] = @token.token
post "/ping", {:public_id => Encryptor.encrypt(@str)}, headers
@job.reload
response.status.should eq 200
@job.last_successful_time.should_not be_nil
end

it "pings with valid token and a base 64 payload" do
headers[JobsController::API_TOKEN_HEADER] = @token.token
post "/ping", {:public_id => Base64.strict_encode64(Encryptor.encrypt(@str)), use_base64: true}, headers
@job.reload
response.status.should eq 200
@job.last_successful_time.should_not be_nil
end
end

it "pings with valid token" do
headers[JobsController::API_TOKEN_HEADER] = @token.token
post "/ping", {:public_id => Encryptor.encrypt(@str)}, headers
@job.reload
response.status.should eq 200
@job.last_successful_time.should_not be_nil
describe "POST v2/ping" do
it "pings with valid token and a base64 encoded payload" do
headers[JobsController::API_TOKEN_HEADER] = @token.token
post '/v2/ping', {:public_id => Base64.strict_encode64(Encryptor.encrypt(@str))}, headers
@job.reload
response.status.should eq 200
@job.last_successful_time.should_not be_nil
end
end
end

end

0 comments on commit 56deac2

Please sign in to comment.