Skip to content

Commit

Permalink
First cut of code for pushing permissions to apps
Browse files Browse the repository at this point in the history
  • Loading branch information
jamiecobbett committed Jul 20, 2012
1 parent 9fbc8cc commit f339848
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 1 deletion.
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ gem 'passphrase_entropy', git: "git://github.com/alphagov/passphrase_entropy.git

gem 'doorkeeper'

gem "gds-api-adapters", "0.2.1"

group :development do
gem 'sqlite3'
end
Expand All @@ -31,5 +33,6 @@ group :test do
gem 'database_cleaner'
gem 'factory_girl_rails'
gem 'shoulda'
gem 'webmock'
end

15 changes: 15 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ GIT
GEM
remote: https://rubygems.org/
specs:
PriorityQueue (0.1.2)
actionmailer (3.1.3)
actionpack (= 3.1.3)
mail (~> 2.3.0)
Expand Down Expand Up @@ -35,6 +36,7 @@ GEM
activesupport (= 3.1.3)
activesupport (3.1.3)
multi_json (~> 1.0)
addressable (2.2.8)
arel (2.2.3)
aws-ses (0.4.4)
builder
Expand All @@ -52,6 +54,7 @@ GEM
xpath (~> 0.1.4)
childprocess (0.3.1)
ffi (~> 1.0.6)
crack (0.3.1)
cucumber (1.1.9)
builder (>= 2.1.2)
diff-lcs (>= 1.1.2)
Expand Down Expand Up @@ -83,6 +86,10 @@ GEM
factory_girl (~> 3.1.0)
railties (>= 3.0.0)
ffi (1.0.11)
gds-api-adapters (0.2.1)
lrucache (~> 0.1.1)
null_logger
plek
gherkin (2.9.3)
json (>= 1.4.6)
hike (1.2.1)
Expand All @@ -91,6 +98,8 @@ GEM
railties (~> 3.0)
thor (~> 0.14)
json (1.6.6)
lrucache (0.1.3)
PriorityQueue (~> 0.1.2)
macaddr (1.6.1)
systemu (~> 2.5.0)
mail (2.3.3)
Expand All @@ -101,6 +110,7 @@ GEM
multi_json (1.2.0)
mysql2 (0.3.11)
nokogiri (1.5.2)
null_logger (0.0.1)
orm_adapter (0.0.7)
plek (0.1.20)
builder
Expand Down Expand Up @@ -160,6 +170,9 @@ GEM
macaddr (~> 1.0)
warden (1.1.1)
rack (>= 1.0)
webmock (1.8.7)
addressable (>= 2.2.7)
crack (>= 0.1.7)
xml-simple (1.1.1)
xpath (0.1.4)
nokogiri (~> 1.3)
Expand All @@ -176,6 +189,7 @@ DEPENDENCIES
doorkeeper
exception_notification
factory_girl_rails
gds-api-adapters (= 0.2.1)
jquery-rails
mysql2
passphrase_entropy!
Expand All @@ -186,3 +200,4 @@ DEPENDENCIES
shoulda
sqlite3
uuid
webmock
5 changes: 5 additions & 0 deletions app/models/enhancements/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ def self.default_permission_strings
def supported_permission_strings
self.class.default_permission_strings + supported_permissions.order(:name).map(&:name)
end

def url_without_path
parsed_url = URI.parse(redirect_uri)
url_without_path = "#{parsed_url.scheme}://#{parsed_url.host}:#{parsed_url.port}"
end
end
5 changes: 5 additions & 0 deletions config/initializers/gds_api_credentials.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
GDS_API_CREDENTIALS = {
basic_auth: {
user: 'api',
password: 'defined_on_rollout_not'
}}
16 changes: 16 additions & 0 deletions lib/gds_api/sso.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
require 'gds_api/base'

class GdsApi::SSO < GdsApi::Base
def initialize(options)
super(nil, options)
end

def update_user(user)
put_json!("#{base_url}/user", JSON.parse(user))
end

private
def base_url
"#{@endpoint}/auth/gds/api"
end
end
42 changes: 42 additions & 0 deletions lib/propagate_permissions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
class PropagatePermissions
def initialize(permissions)
@user = permissions.first.user
@applications = permissions.map(&:application)
end

def attempt
results = { successes: [], failures: [] }
@applications.each do |application|
begin
update_application(@user, application)
results[:successes] << { application: application }
rescue URI::InvalidURIError
results[:failures] << { application: application, message: "Haven't got a valid URL for that app.", technical: "URL I have is: #{application.redirect_uri}" }
rescue GdsApi::EndpointNotFound, SocketError => e
results[:failures] << { application: application, message: "Couldn't find the app. Maybe the app is down?", technical: e.message }
rescue GdsApi::TimedOutException
results[:failures] << { application: application, message: "Timed out. Maybe the app is down?" }
rescue GdsApi::HTTPErrorResponse => e
message = case e.code
when 404
"This app doesn't seem to support syncing of permissions."
when 502
"Couldn't find the app. Maybe the app is down?"
else
e.message
end
results[:failures] << { application: application, message: message, technical: "HTTP status code was: #{e.code}" }
rescue GdsApi::BaseError, StandardError => e
results[:failures] << { application: application, message: e.message }
end
end
results
end

private
def update_application(user, application)
options = { endpoint_url: application.url_without_path }.merge(GDS_API_CREDENTIALS)
api = GdsApi::SSO.new(options)
api.update_user(user.to_sensible_json)
end
end
9 changes: 8 additions & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,22 @@
require 'rails/test_help'

require 'shoulda'
require 'webmock/minitest'

WebMock.disable_net_connect!(:allow_localhost => true)

class ActiveSupport::TestCase
# Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
#
# Note: You'll currently still have to declare fixtures explicitly in integration tests
# -- they do not yet inherit this setting
fixtures :all
# fixtures :all

# Add more helper methods to be used by all tests here...

teardown do
WebMock.reset!
end
end

class ActionController::TestCase
Expand Down
60 changes: 60 additions & 0 deletions test/unit/propagate_permissions_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require 'test_helper'

class PropagatePermissionsTest < ActiveSupport::TestCase

def url_for_app(application)
url = URI.parse(application.redirect_uri)
"http://api:defined_on_rollout_not@#{url.host}/auth/gds/api/user"
end

setup do
@user = FactoryGirl.create(:user)
@application = FactoryGirl.create(:application, redirect_uri: "http://app.com/callback")
@permission = FactoryGirl.create(:permission,
application: @application,
user: @user,
permissions: ["ba"])
end

should "send a PUT to the related app with the user.json as in the OAuth exchange" do
expected_body = @user.to_sensible_json
expected_url =
request = stub_request(:put, url_for_app(@application)).with(body: expected_body)
PropagatePermissions.new([@permission]).attempt
assert_requested request
end

should "return a structure of successful and failed pushes" do
not_supported_yet_app = FactoryGirl.create(:application, redirect_uri: "http://not-supported-yet.com/callback")
FactoryGirl.create(:permission,
application: not_supported_yet_app,
user: @user,
permissions: ["ba"])

slow_app = FactoryGirl.create(:application, redirect_uri: "http://slow.com/callback")
FactoryGirl.create(:permission,
application: slow_app,
user: @user,
permissions: ["ba"])

stub_request(:put, url_for_app(@application)).to_return(status: 200)
stub_request(:put, url_for_app(not_supported_yet_app)).to_return(status: 404)
stub_request(:put, url_for_app(slow_app)).to_timeout

results = PropagatePermissions.new(@user.permissions).attempt

assert_equal [{ application: @application }], results[:successes]
expected_failures = [
{
application: not_supported_yet_app,
message: "This app doesn't seem to support syncing of permissions.",
technical: "HTTP status code was: 404"
},
{
application: slow_app,
message: "Timed out. Maybe the app is down?"
}
]
assert_equal expected_failures, results[:failures]
end
end

0 comments on commit f339848

Please sign in to comment.