chowder 0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/chowder.rb +176 -0
  2. data/lib/sinatra/chowder.rb +28 -0
  3. metadata +76 -0
data/lib/chowder.rb ADDED
@@ -0,0 +1,176 @@
1
+ require 'sinatra/base'
2
+ require 'ostruct'
3
+ require 'openid'
4
+ require 'openid/store/filesystem'
5
+
6
+ module Chowder
7
+ class Base < Sinatra::Base
8
+ disable :raise_errors
9
+
10
+ LOGIN_VIEW = <<-HTML
11
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
12
+ <html lang='en-us' xmlns='http://www.w3.org/1999/xhtml'>
13
+ <head><title>Log In</title></head>
14
+ <body>
15
+ <form action="/login" method="post">
16
+ <div id="basic_login_field">
17
+ <label for="login">Login: </label>
18
+ <input id="login" type="text" name="login" /><br />
19
+ </div>
20
+ <div id="basic_password_field">
21
+ <label for="password">Password: </label>
22
+ <input id="password" type="password" name="password" /><br />
23
+ </div>
24
+ <div id="basic_login_button">
25
+ <input type="submit" value="Login" />
26
+ </div>
27
+ </form>
28
+ <p>OpenID:</p>
29
+ <form action="/openid/initiate" method="post">
30
+ <div id="openid_login_field">
31
+ <label for="openid_identifier">URL: </label>
32
+ <input id="openid_identifier" type="text" name="openid_identifier" /><br />
33
+ </div>
34
+ <div id="openid_login_button">
35
+ <input type="submit" value="Login" />
36
+ </div>
37
+ </form>
38
+ </body></html>
39
+ HTML
40
+
41
+ SIGNUP_VIEW = <<-HTML
42
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
43
+ <html lang='en-us' xmlns='http://www.w3.org/1999/xhtml'>
44
+ <head><title>Sign Up</title></head>
45
+ <body>
46
+ __ERRORS__
47
+ <form action="/signup" method="post">
48
+ <div id="basic_login_field">
49
+ <label for="login">Login: </label>
50
+ <input id="login" type="text" name="login" /><br />
51
+ </div>
52
+ <div id="basic_password_field">
53
+ <label for="password">Password: </label>
54
+ <input id="password" type="password" name="password" /><br />
55
+ </div>
56
+ <div id="basic_signup_button">
57
+ <input type="submit" value="Sign Up" />
58
+ </div>
59
+ </form>
60
+ </body></html>
61
+ HTML
62
+
63
+ def self.new(app=nil, args={}, &block)
64
+ builder = Rack::Builder.new
65
+ builder.use Rack::Session::Cookie, :secret => args[:secret]
66
+ builder.run super
67
+ builder.to_app
68
+ end
69
+
70
+ def initialize(app=nil, args={}, &block)
71
+ @signup_callback = args[:signup]
72
+ @login_callback = args[:login] || block
73
+ super(app)
74
+ end
75
+
76
+ def authorize(user)
77
+ session[:current_user] = user
78
+ end
79
+
80
+ def return_or_redirect_to(path)
81
+ redirect(session[:return_to] || path)
82
+ end
83
+
84
+ def render_custom_template(type)
85
+ views_dir = self.options.views || "./views"
86
+ template = Dir[File.join(views_dir, "#{type}.*")].first
87
+ if template
88
+ engine = File.extname(template)[1..-1]
89
+ send(engine, type)
90
+ end
91
+ end
92
+
93
+ get '/login' do
94
+ render_custom_template(:login) || LOGIN_VIEW
95
+ end
96
+
97
+ get '/logout' do
98
+ session[:current_user] = nil
99
+ redirect '/'
100
+ end
101
+ end
102
+
103
+ class Basic < Base
104
+ post '/login' do
105
+ login, password = params['login'], params['password']
106
+ if authorize @login_callback.call(login, password)
107
+ return_or_redirect_to '/'
108
+ else
109
+ redirect '/login'
110
+ end
111
+ end
112
+
113
+ get '/signup' do
114
+ if @signup_callback
115
+ render_custom_template(:signup) || signup_view_with_errors([])
116
+ else
117
+ throw :pass
118
+ end
119
+ end
120
+
121
+ post '/signup' do
122
+ throw :pass unless @signup_callback
123
+
124
+ # results is either [true, <userid>] or [false, <errors>]
125
+ successful_signup, *extras = @signup_callback.call(params)
126
+ if successful_signup
127
+ authorize extras[0]
128
+ return_or_redirect_to '/'
129
+ else
130
+ @errors = extras
131
+ render_custom_template(:signup) || signup_view_with_errors(extras)
132
+ SIGNUP_VIEW.gsub(
133
+ /__ERRORS__/,
134
+ @errors.map { |e| "<p class=\"error\">#{e}</p>" }.join("\n")
135
+ )
136
+ end
137
+ end
138
+
139
+ private
140
+ def signup_view_with_errors(errors)
141
+ SIGNUP_VIEW.gsub(
142
+ /__ERRORS__/,
143
+ errors.map { |e| "<p class=\"error\">#{e}</p>" }.join("\n")
144
+ )
145
+ end
146
+ end
147
+
148
+ class OpenID < Base
149
+ def host
150
+ host = env['HTTP_HOST'] || "#{env['SERVER_NAME']}:#{env['SERVER_PORT']}"
151
+ "http://#{host}"
152
+ end
153
+
154
+ def setup_consumer
155
+ store = ::OpenID::Store::Filesystem.new('.openid')
156
+ osession = session[:openid] ||= {}
157
+ @consumer = ::OpenID::Consumer.new(osession, store)
158
+ end
159
+
160
+ post '/openid/initiate' do
161
+ setup_consumer
162
+ url = @consumer.begin(params['openid_identifier']).redirect_url(host, host + '/openid/authenticate')
163
+ redirect url
164
+ end
165
+
166
+ get '/openid/authenticate' do
167
+ setup_consumer
168
+ res = @consumer.complete(request.params, host + '/openid/authenticate')
169
+ user = @login_callback.call(res.identity_url)
170
+ if res.is_a?(::OpenID::Consumer::SuccessResponse) && authorize(user)
171
+ return_or_redirect_to '/'
172
+ end
173
+ redirect '/login'
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,28 @@
1
+ require 'sinatra/base'
2
+
3
+ module Sinatra
4
+ module Chowder
5
+ def current_user
6
+ session[:current_user]
7
+ end
8
+
9
+ def authorized?
10
+ session[:current_user]
11
+ end
12
+
13
+ def login
14
+ session[:redirect_to] = request.path_info
15
+ redirect '/login'
16
+ end
17
+
18
+ def logout
19
+ session[:current_user] = nil
20
+ end
21
+
22
+ def require_user
23
+ login unless authorized?
24
+ end
25
+ end
26
+
27
+ helpers Chowder
28
+ end
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chowder
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.2"
5
+ platform: ruby
6
+ authors:
7
+ - Harry Vangberg
8
+ - Sam Merritt
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-03-11 00:00:00 +01:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: sinatra
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 0.9.1
25
+ version:
26
+ - !ruby/object:Gem::Dependency
27
+ name: ruby-openid
28
+ type: :runtime
29
+ version_requirement:
30
+ version_requirements: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: "0"
35
+ version:
36
+ description:
37
+ email: harry@vangberg.name
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files: []
43
+
44
+ files:
45
+ - lib/chowder.rb
46
+ - lib/sinatra/chowder.rb
47
+ has_rdoc: true
48
+ homepage: http://github.com/ichverstehe/chowder
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: "0"
61
+ version:
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ requirements: []
69
+
70
+ rubyforge_project:
71
+ rubygems_version: 1.3.5
72
+ signing_key:
73
+ specification_version: 3
74
+ summary: rack middleware providing session based authentication
75
+ test_files: []
76
+