rack / sso - meetupfiles.meetup.com/437842/svrug-november-2009.pdfrack / sso and a little bit about...
TRANSCRIPT
RACK / SSOand a little bit about the Bundler
Tuesday, December 1, 2009
hello hello
COREY DONOHOE@atmos / [email protected]
Tuesday, December 1, 2009
i’m a software developer and open source participant for a number of yearsthis is me in hawaii last month with a much more awesome beard
ENGINE YARDTuesday, December 1, 2009
i’ve been at engineyard for 2.5 yearsdid support for the first year, now doing internal developmentwe do a lot of microapps
HANCOCK
Tuesday, December 1, 2009
will smith was not involved in the creation of this project
Tuesday, December 1, 2009
like your john hancocknot for everyone but i’m hoping to keep it ongoing as a working example
RACKTuesday, December 1, 2009
it’s what everyone is using now, rails, merb, sinatra, ramazerealizing the power of rack helped me really embrace the simplicity of sinatra
SINATRA IS AWESOME
Tuesday, December 1, 2009
2009 has mainly been sinatra and datamapper for usthere’s still 2 merb apps we maintain, one of them is our SSO provider
GIT://GITHUB.COM/ATMOS/HANCOCK.GIT
Tuesday, December 1, 2009
SINGLE SIGN ON
Tuesday, December 1, 2009
TRADITIONAL OPENID
Tuesday, December 1, 2009
user agent == browserconsumer == app using openid to authenticate clientsprovider == openid provider the consumer talks to
TRADITIONAL OPENID
Tuesday, December 1, 2009
no more specifying who you areno more consumer <-> provider identity negotiationno more choosing of profiles on the server
HANCOCK NEGOTIATION
Tuesday, December 1, 2009
by cutting those out there’s fewer redirectsmuch easier to see what portions of the openid spec are usedstill conforms to the spec so it should be easily accessible from any language
# ~/p/hancock/bin/shotgun -p PORT config.rurequire 'hancock'
DataMapper.setup(:default, "sqlite3://#{File.dirname(__FILE__)}/development.rb")DataMapper.auto_migrate!
Hancock::Consumer.create(:url => 'http://localhost:3000/sso/login', :label => 'Rails Dev', :internal => false)Hancock::Consumer.create(:url => 'http://localhost:9292/sso/login', :label => 'Rack Fans', :internal => false)Hancock::Consumer.create(:url => 'http://localhost:9393/sso/login', :label => 'Shotgun Fans', :internal => false)
class Dragon < Hancock::App get '/' do redirect '/sso/login' unless session['hancock_server_user_id'] erb "<h2>Hello <%= session_user.name %><!-- <%= session.inspect %>" endendrun Dragon
run Hancock::App
Tuesday, December 1, 2009
example rackup file
HANCOCK-CLIENT
Tuesday, December 1, 2009
there’s a client middleware for consumersmicroapps are great for spikes that can expose business valuewith something like this we can spin up microapps quickly
require File.join(File.dirname(__FILE__), 'boot')
Rails::Initializer.run do |config| config.gem 'hancock', :lib => 'hancock'
config.middleware.use Hancock::Client::Middleware do |sso| sso.sso_url = 'http://localhost:20000' end # all your other normal stuffend
hancock-client-rails
Tuesday, December 1, 2009
middleware like this works in rails too in versions > 2.3.xyou can either use the use keyword or the the generators for rails metal
UNDER THE HOODTuesday, December 1, 2009
it’s what everyone is using now, rails, merb, sinatra, ramazerealizing the power of rack helped me really embrace the simplicity of sinatra
HTTP://GITHUB.COM/
Tuesday, December 1, 2009
use
Tuesday, December 1, 2009
specific middlewareuse invokes initialize methodit can take a block
use in Sinatra
Tuesday, December 1, 2009
initialize arity for usefirst parameter is the middleware constantinitializing with a block can do cool things.
#!/usr/bin/env rackup
use EY::SSO do |sso| sso.only_staff!enduse EY::ContactManager
Tuesday, December 1, 2009
use in Rails
Tuesday, December 1, 2009
require File.join(File.dirname(__FILE__), 'boot')
Rails::Initializer.run do |config| config.gem 'hancock', :lib => 'hancock'
config.middleware.use Hancock::Client::Middleware do |sso| sso.sso_url = 'http://localhost:20000' end # all your other normal stuffend
config.middleware.use
Tuesday, December 1, 2009
# Allow the metal piece to run in isolationrequire(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)require 'hancock-client'
class Sso < Hancock::Client::Default disable :raise_errors set :sso_url, 'http://hancock.atmos.org/sso'end
script/generate metal sso
Tuesday, December 1, 2009
works fine for inheriting from Sinatra::Defaultso you can write sinatra in rails if you want :)
map
Tuesday, December 1, 2009
map is a way to ‘mount’ applicationswe use it for everyday kinds of things
helpers do def url(path) request.script_name + path endend
Tuesday, December 1, 2009
#!/usr/bin/env rackup
require File.dirname(__FILE__) + '/lib/setup'require 'gateway/app'require 'migration/app'
use Rack::Static, :urls => ["/css", "/img", "/js"], :root => "public"
map "/gateway/" do use EY::SSO run Gateway::Append
map "/migration/" do use Rack::ShowExceptions if ENV["RACK_ENV"] == "production" use EY::SSO do |sso| sso.only_staff! end end run Migration::Append
map "/" do app = lambda do |env| [404, {"Content-Type" => "text/plain", "Content-Length" => "9"}, ["Not found"]] end run append
Tuesday, December 1, 2009
two separate apps, Migration::App and Gateway::App working fine together on subdir mappings
PROBLEMS
http://www.flickr.com/photos/northover/3022796561/
Tuesday, December 1, 2009
FRAGILE
http://www.flickr.com/photos/northover/3022796561/
Tuesday, December 1, 2009
most fragile point is our integration with our CRM system, salesforcewe have datamapper models but keeping things in sync proves to be crazywe use hoptoad and are diligent about resolving any exceptions we receive
CONFUSING
http://www.flickr.com/photos/northover/3022796561/
Tuesday, December 1, 2009
usability and accessibility can easily be neglected, users get confusednot being able to get every system on to something centralized leads to more confusionif you’re going to go this route spend a chunk of time on the UX
TESTING
http://www.flickr.com/photos/northover/3022796561/
Tuesday, December 1, 2009
how does SSO really behave today, will it behave that way tomorrow?providing a decent client API makes it easy to test, provide mocks instead of vice versamake it trivial to test so people actually do, cuts down on support
ADAPTERS MAKE IT EASY
Tuesday, December 1, 2009
adapters allow us to pick the backend we want to test at any timewe don’t write tests to do a full integration test, we just flip a switch to turn on a different adapterthis allows us to iterate quickly on in memory mocks and run everything when the feature works
SECURITY
http://www.flickr.com/photos/northover/3022796561/
Tuesday, December 1, 2009
sessions are cookie sessions, so they’re difficult to expire on consumersthere is https, the consumer whitelist allows and communicates over https :)single sign out sucks because it requires a shared domain cookie
YAY, COOKIE SESSIONS
Tuesday, December 1, 2009
THE BUNDLERTuesday, December 1, 2009
how to deploy your new baby and ensure it’ll play well with othersthis is how we manage lots of apps dependencies in a repeatable mannerthink merb’s bundling that actually works.
THE BUNDLERTuesday, December 1, 2009
people can and will do crazy shit with rubygems.it’s available on github under the wycats usercarl lerche has done a tremendous job keeping all of the insanity in order
DEPLOYING AN APP
gem 'rack_hoptoad', '>=0.0.3'gem 'sinatra', '~>0.9.4', :require_as => 'sinatra/base'gem 'rest-client', :require_as => 'rest_client'gem 'json'gem 'dm-core'gem 'dm-validations'gem 'do_sqlite3'
only :test do gem 'rake' gem 'rspec', :require_as => %w(spec) gem 'rcov' gem 'bundler', '>=0.5.0' gem 'cucumber' gem 'webrat', '~>0.5.0' gem 'rack-test', '~>0.5.0', :require_as => 'rack/test' gem 'fakeweb', '>=1.2.5' gem 'ParseTree', '>=3.0.4', :require_as => 'parse_tree' gem 'randexp', '>=0.1.4'end
disable_system_gems
# vim:ft=ruby
Tuesday, December 1, 2009
specify the gems you need required at runtime in a global scopeall of your normal gem version hacks work as expectedawkward requires can be handled easily as an array or single string
BIN_PATH
gem 'sinatra', '~>0.9.0', :require_as => [ ]gem 'haml', '~>2.2.0', :require_as => [ ]gem 'do_sqlite3', '~>0.9.12', :require_as => [ ]gem 'dm-validations', '~>0.9.11', :require_as => [ ]gem 'dm-timestamps', '~>0.9.11', :require_as => [ ]gem 'dm-types', '~>0.9.11', :require_as => [ ]gem 'ruby-openid', '~>2.1.7', :require_as => [ ]gem 'guid', '~>0.1.1', :require_as => [ ]gem 'rack-contrib', '~>0.9.2', :require_as => [ ]gem 'json', :require_as => [ ]
only :test do gem 'rack-test', '~>0.5.0', :require_as => 'rack/test' gem 'webrat', '~>0.5.0' gem 'rspec', '~>1.2.9', :require_as => 'spec' gem 'rake' gem 'rcov' gem 'cucumber' gem 'dm-aggregates', '~>0.9.11' gem 'dm-sweatshop', '~>0.9.11' gem 'randexp' gem 'ParseTree', :require_as => 'parse_tree' gem 'bundler', '>=0.6.0'end
bin_path 'gbin'disable_system_gems
Tuesday, December 1, 2009
note the bin_path stuff, lots of gems distribute executables in bin, gbin stands for gem bindouble check how you’re bundling executables in your gems, you could easily overwrite your system rake.
DISABLE_SYSTEM_GEMS
gem 'sinatra', '~>0.9.0', :require_as => [ ]gem 'haml', '~>2.2.0', :require_as => [ ]gem 'do_sqlite3', '~>0.9.12', :require_as => [ ]gem 'dm-validations', '~>0.9.11', :require_as => [ ]gem 'dm-timestamps', '~>0.9.11', :require_as => [ ]gem 'dm-types', '~>0.9.11', :require_as => [ ]gem 'ruby-openid', '~>2.1.7', :require_as => [ ]gem 'guid', '~>0.1.1', :require_as => [ ]gem 'rack-contrib', '~>0.9.2', :require_as => [ ]gem 'json', :require_as => [ ]
only :test do gem 'rack-test', '~>0.5.0', :require_as => 'rack/test' gem 'webrat', '~>0.5.0' gem 'rspec', '~>1.2.9', :require_as => 'spec' gem 'rake' gem 'rcov' gem 'cucumber' gem 'dm-aggregates', '~>0.9.11' gem 'dm-sweatshop', '~>0.9.11' gem 'randexp' gem 'ParseTree', :require_as => 'parse_tree' gem 'bundler', '>=0.6.0'end
bin_path 'gbin'disable_system_gems
Tuesday, December 1, 2009
turns out to be very useful as there won’t be any system gem conflictskeeps each application nice, local and repeatably deployed.
SPEC/SPEC_HELPER.RB
Bundler.require_env(:test)require File.join(File.dirname(__FILE__), '..', 'lib', 'myapp')require 'pp'
DataMapper.setup(:default, 'sqlite3://:memory:')DataMapper.auto_migrate!
Spec::Runner.configure do |config| config.include(Rack::Test::Methods) def app MyApp.app endend
Tuesday, December 1, 2009
explicitly call Bundler.require_env(:test) to include that ‘only’ block from beforerequire your application and all is well.
CONFIG.RU
Bundler.require_envrequire File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'my_app'))
DataMapper.setup(:default, 'sqlite3://:memory:')DataMapper.auto_migrate!
run MyApp.app
Tuesday, December 1, 2009
using Bundler.require_env sets you up perfectlyit runs fine under passengerother executables should be used from the bin_path directorytring
DEPLOYING A GEM
gem 'sinatra', '~>0.9.0', :require_as => [ ]gem 'haml', '~>2.2.0', :require_as => [ ]gem 'do_sqlite3', '~>0.9.12', :require_as => [ ]gem 'dm-validations', '~>0.9.11', :require_as => [ ]gem 'dm-timestamps', '~>0.9.11', :require_as => [ ]gem 'dm-types', '~>0.9.11', :require_as => [ ]gem 'ruby-openid', '~>2.1.7', :require_as => [ ]gem 'guid', '~>0.1.1', :require_as => [ ]gem 'rack-contrib', '~>0.9.2', :require_as => [ ]gem 'json', :require_as => [ ]
only :test do gem 'rack-test', '~>0.5.0', :require_as => 'rack/test' gem 'webrat', '~>0.5.0' gem 'rspec', '~>1.2.9', :require_as => 'spec' gem 'rake' gem 'rcov' gem 'cucumber' gem 'dm-aggregates', '~>0.9.11' gem 'dm-sweatshop', '~>0.9.11' gem 'randexp' gem 'ParseTree', :require_as => 'parse_tree' gem 'bundler', '>=0.6.0'end
bin_path 'gbin'disable_system_gems
Tuesday, December 1, 2009
specify the gems you need required at runtime in a global scope but don’t require themyour application code should require them and cause bad deployments.
YOUR RAKE FILE
require 'rake/gempackagetask'require 'rubygems/specification'require 'date'require 'bundler'
spec = Gem::Specification.new do |s| ...
manifest = Bundler::Environment.load(File.dirname(__FILE__) + '/Gemfile') manifest.dependencies.each do |d| next if d.only && d.only.include?('test') s.add_dependency(d.name, d.version) endend
Tuesday, December 1, 2009
you can require the bundler and use your manifest file to gem dependenciesthis way you only maintain it in one spotyou could also do development dependencies if you’d like
IDENTITY
http://www.flickr.com/photos/dullhunk/3953605716/
Tuesday, December 1, 2009
lots of other realms of identity involved to really get it all right.
IDENTITY
http://www.flickr.com/photos/dullhunk/3953605716/
Tuesday, December 1, 2009
security + authentication + information
IDENTITY
http://www.xmlgrrl.com/blog/2009/10/02/a-venn-of-identity-in-web-services-now-with-oauth/
Tuesday, December 1, 2009
a slightly updated version uses oauth, we do a similar thing with the oauth provider.our next wave is to take advantage of
THANKS SO MUCH!
Tuesday, December 1, 2009
QUESTIONS?
COMMENTS?
Tuesday, December 1, 2009