arrow 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +1590 -0
- data/LICENSE +28 -0
- data/README +75 -0
- data/Rakefile +366 -0
- data/Rakefile.local +63 -0
- data/data/arrow/applets/TEMPLATE.rb.tpl +53 -0
- data/data/arrow/applets/args.rb +50 -0
- data/data/arrow/applets/config.rb +55 -0
- data/data/arrow/applets/error.rb +63 -0
- data/data/arrow/applets/files.rb +46 -0
- data/data/arrow/applets/inspect.rb +46 -0
- data/data/arrow/applets/nosuchapplet.rb +31 -0
- data/data/arrow/applets/status.rb +92 -0
- data/data/arrow/applets/test.rb +133 -0
- data/data/arrow/applets/tutorial/counter.rb +96 -0
- data/data/arrow/applets/tutorial/dingus.rb +67 -0
- data/data/arrow/applets/tutorial/hello.rb +34 -0
- data/data/arrow/applets/tutorial/hello2.rb +73 -0
- data/data/arrow/applets/tutorial/imgtext.rb +90 -0
- data/data/arrow/applets/tutorial/imgtext2.rb +286 -0
- data/data/arrow/applets/tutorial/index.rb +36 -0
- data/data/arrow/applets/tutorial/logo.rb +98 -0
- data/data/arrow/applets/tutorial/memcache.rb +61 -0
- data/data/arrow/applets/tutorial/missing.rb +37 -0
- data/data/arrow/applets/tutorial/protected.rb +100 -0
- data/data/arrow/applets/tutorial/redirector.rb +52 -0
- data/data/arrow/applets/tutorial/rndimages.rb +159 -0
- data/data/arrow/applets/tutorial/sharenotes.rb +83 -0
- data/data/arrow/applets/tutorial/subclassed-hello.rb +32 -0
- data/data/arrow/applets/tutorial/superhello.rb +72 -0
- data/data/arrow/applets/tutorial/timeclock.rb +78 -0
- data/data/arrow/applets/view-applet.rb +123 -0
- data/data/arrow/applets/view-template.rb +85 -0
- data/data/arrow/applets/wiki.rb +274 -0
- data/data/arrow/templates/TEMPLATE.tmpl.tpl +36 -0
- data/data/arrow/templates/applet-status.tmpl +153 -0
- data/data/arrow/templates/args-display.tmpl +120 -0
- data/data/arrow/templates/config/display-table.tmpl +36 -0
- data/data/arrow/templates/config/display.tmpl +36 -0
- data/data/arrow/templates/counter-deleted.tmpl +33 -0
- data/data/arrow/templates/counter.tmpl +59 -0
- data/data/arrow/templates/dingus.tmpl +55 -0
- data/data/arrow/templates/enumtable.tmpl +8 -0
- data/data/arrow/templates/error-display.tmpl +92 -0
- data/data/arrow/templates/filemap.tmpl +89 -0
- data/data/arrow/templates/hello-world-src.tmpl +34 -0
- data/data/arrow/templates/hello-world.tmpl +60 -0
- data/data/arrow/templates/imgtext/fontlist.tmpl +46 -0
- data/data/arrow/templates/imgtext/form.tmpl +70 -0
- data/data/arrow/templates/imgtext/reload-error.tmpl +40 -0
- data/data/arrow/templates/imgtext/reload.tmpl +55 -0
- data/data/arrow/templates/inspect/display.tmpl +80 -0
- data/data/arrow/templates/loginform.tmpl +64 -0
- data/data/arrow/templates/logout.tmpl +32 -0
- data/data/arrow/templates/memcache/display.tmpl +41 -0
- data/data/arrow/templates/navbar.incl +27 -0
- data/data/arrow/templates/nosuchapplet.tmpl +32 -0
- data/data/arrow/templates/printsource.tmpl +35 -0
- data/data/arrow/templates/protected.tmpl +36 -0
- data/data/arrow/templates/rndimages.tmpl +38 -0
- data/data/arrow/templates/service-response.tmpl +13 -0
- data/data/arrow/templates/sharenotes/display.tmpl +38 -0
- data/data/arrow/templates/status.tmpl +120 -0
- data/data/arrow/templates/templateviewer.tmpl +43 -0
- data/data/arrow/templates/test/harness.tmpl +57 -0
- data/data/arrow/templates/test/list.tmpl +48 -0
- data/data/arrow/templates/test/problem.tmpl +42 -0
- data/data/arrow/templates/tutorial/index.tmpl +37 -0
- data/data/arrow/templates/tutorial/missingapplet.tmpl +29 -0
- data/data/arrow/templates/view-applet-nosuch.tmpl +32 -0
- data/data/arrow/templates/view-applet.tmpl +40 -0
- data/data/arrow/templates/view-template.tmpl +83 -0
- data/data/arrow/templates/wiki/formerror.tmpl +47 -0
- data/data/arrow/templates/wiki/markup_help.incl +6 -0
- data/data/arrow/templates/wiki/new.tmpl +56 -0
- data/data/arrow/templates/wiki/new_system.tmpl +122 -0
- data/data/arrow/templates/wiki/sectionlist.tmpl +43 -0
- data/data/arrow/templates/wiki/show.tmpl +34 -0
- data/docs/manual/layouts/default.page +43 -0
- data/docs/manual/lib/api-filter.rb +81 -0
- data/docs/manual/lib/editorial-filter.rb +64 -0
- data/docs/manual/lib/examples-filter.rb +244 -0
- data/docs/manual/lib/links-filter.rb +117 -0
- data/lib/apache/fakerequest.rb +448 -0
- data/lib/apache/logger.rb +33 -0
- data/lib/arrow.rb +51 -0
- data/lib/arrow/acceptparam.rb +207 -0
- data/lib/arrow/applet.rb +725 -0
- data/lib/arrow/appletmixins.rb +218 -0
- data/lib/arrow/appletregistry.rb +590 -0
- data/lib/arrow/applettestcase.rb +503 -0
- data/lib/arrow/broker.rb +255 -0
- data/lib/arrow/cache.rb +176 -0
- data/lib/arrow/config-loaders/yaml.rb +75 -0
- data/lib/arrow/config.rb +615 -0
- data/lib/arrow/constants.rb +24 -0
- data/lib/arrow/cookie.rb +359 -0
- data/lib/arrow/cookieset.rb +108 -0
- data/lib/arrow/dispatcher.rb +368 -0
- data/lib/arrow/dispatcherloader.rb +50 -0
- data/lib/arrow/exceptions.rb +61 -0
- data/lib/arrow/fallbackhandler.rb +48 -0
- data/lib/arrow/formvalidator.rb +631 -0
- data/lib/arrow/htmltokenizer.rb +343 -0
- data/lib/arrow/logger.rb +488 -0
- data/lib/arrow/logger/apacheoutputter.rb +69 -0
- data/lib/arrow/logger/arrayoutputter.rb +63 -0
- data/lib/arrow/logger/coloroutputter.rb +111 -0
- data/lib/arrow/logger/fileoutputter.rb +96 -0
- data/lib/arrow/logger/htmloutputter.rb +54 -0
- data/lib/arrow/logger/outputter.rb +123 -0
- data/lib/arrow/mixins.rb +425 -0
- data/lib/arrow/monkeypatches.rb +94 -0
- data/lib/arrow/object.rb +117 -0
- data/lib/arrow/path.rb +196 -0
- data/lib/arrow/service.rb +447 -0
- data/lib/arrow/session.rb +289 -0
- data/lib/arrow/session/dbstore.rb +100 -0
- data/lib/arrow/session/filelock.rb +160 -0
- data/lib/arrow/session/filestore.rb +132 -0
- data/lib/arrow/session/id.rb +98 -0
- data/lib/arrow/session/lock.rb +253 -0
- data/lib/arrow/session/md5id.rb +42 -0
- data/lib/arrow/session/nulllock.rb +42 -0
- data/lib/arrow/session/posixlock.rb +166 -0
- data/lib/arrow/session/sha1id.rb +54 -0
- data/lib/arrow/session/store.rb +366 -0
- data/lib/arrow/session/usertrackid.rb +52 -0
- data/lib/arrow/spechelpers.rb +73 -0
- data/lib/arrow/template.rb +713 -0
- data/lib/arrow/template/attr.rb +31 -0
- data/lib/arrow/template/call.rb +31 -0
- data/lib/arrow/template/comment.rb +33 -0
- data/lib/arrow/template/container.rb +118 -0
- data/lib/arrow/template/else.rb +41 -0
- data/lib/arrow/template/elsif.rb +44 -0
- data/lib/arrow/template/escape.rb +53 -0
- data/lib/arrow/template/export.rb +87 -0
- data/lib/arrow/template/for.rb +145 -0
- data/lib/arrow/template/if.rb +78 -0
- data/lib/arrow/template/import.rb +119 -0
- data/lib/arrow/template/include.rb +206 -0
- data/lib/arrow/template/iterator.rb +208 -0
- data/lib/arrow/template/nodes.rb +734 -0
- data/lib/arrow/template/parser.rb +571 -0
- data/lib/arrow/template/prettyprint.rb +53 -0
- data/lib/arrow/template/render.rb +191 -0
- data/lib/arrow/template/selectlist.rb +94 -0
- data/lib/arrow/template/set.rb +87 -0
- data/lib/arrow/template/timedelta.rb +81 -0
- data/lib/arrow/template/unless.rb +78 -0
- data/lib/arrow/template/urlencode.rb +51 -0
- data/lib/arrow/template/yield.rb +139 -0
- data/lib/arrow/templatefactory.rb +125 -0
- data/lib/arrow/testcase.rb +567 -0
- data/lib/arrow/transaction.rb +608 -0
- data/rake/191_compat.rb +26 -0
- data/rake/dependencies.rb +76 -0
- data/rake/documentation.rb +114 -0
- data/rake/helpers.rb +502 -0
- data/rake/hg.rb +282 -0
- data/rake/manual.rb +787 -0
- data/rake/packaging.rb +129 -0
- data/rake/publishing.rb +278 -0
- data/rake/style.rb +62 -0
- data/rake/svn.rb +668 -0
- data/rake/testing.rb +187 -0
- data/rake/verifytask.rb +64 -0
- data/spec/arrow/acceptparam_spec.rb +157 -0
- data/spec/arrow/applet_spec.rb +575 -0
- data/spec/arrow/appletmixins_spec.rb +409 -0
- data/spec/arrow/appletregistry_spec.rb +294 -0
- data/spec/arrow/broker_spec.rb +153 -0
- data/spec/arrow/config_spec.rb +224 -0
- data/spec/arrow/cookieset_spec.rb +164 -0
- data/spec/arrow/dispatcher_spec.rb +137 -0
- data/spec/arrow/dispatcherloader_spec.rb +65 -0
- data/spec/arrow/formvalidator_spec.rb +781 -0
- data/spec/arrow/logger_spec.rb +346 -0
- data/spec/arrow/mixins_spec.rb +120 -0
- data/spec/arrow/service_spec.rb +645 -0
- data/spec/arrow/session_spec.rb +121 -0
- data/spec/arrow/template/iterator_spec.rb +222 -0
- data/spec/arrow/templatefactory_spec.rb +185 -0
- data/spec/arrow/transaction_spec.rb +319 -0
- data/spec/arrow_spec.rb +37 -0
- data/spec/lib/appletmatchers.rb +281 -0
- data/spec/lib/constants.rb +77 -0
- data/spec/lib/helpers.rb +41 -0
- data/spec/lib/matchers.rb +44 -0
- data/tests/cookie.tests.rb +310 -0
- data/tests/path.tests.rb +157 -0
- data/tests/session.tests.rb +111 -0
- data/tests/session_id.tests.rb +82 -0
- data/tests/session_lock.tests.rb +191 -0
- data/tests/session_store.tests.rb +53 -0
- data/tests/template.tests.rb +1360 -0
- metadata +339 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
BEGIN {
|
|
4
|
+
require 'pathname'
|
|
5
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
|
6
|
+
|
|
7
|
+
libdir = basedir + "lib"
|
|
8
|
+
|
|
9
|
+
$LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
require 'spec'
|
|
14
|
+
require 'spec/lib/constants'
|
|
15
|
+
require 'spec/lib/helpers'
|
|
16
|
+
require 'spec/lib/appletmatchers'
|
|
17
|
+
|
|
18
|
+
require 'arrow'
|
|
19
|
+
require 'arrow/applet'
|
|
20
|
+
require 'arrow/appletmixins'
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
include Arrow::TestConstants
|
|
24
|
+
|
|
25
|
+
#####################################################################
|
|
26
|
+
### C O N T E X T S
|
|
27
|
+
#####################################################################
|
|
28
|
+
|
|
29
|
+
describe Arrow::AppletAuthentication do
|
|
30
|
+
include Arrow::SpecHelpers,
|
|
31
|
+
Arrow::AppletMatchers
|
|
32
|
+
|
|
33
|
+
before( :all ) do
|
|
34
|
+
setup_logging( :crit )
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
after( :all ) do
|
|
38
|
+
reset_logging()
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
before( :each ) do
|
|
43
|
+
@uri = '/testing'
|
|
44
|
+
@connection = stub( "apache connnection", :remote_host => 'host' )
|
|
45
|
+
@txn = stub( "transaction", :uri => @uri, :vargs => true, :user => nil,
|
|
46
|
+
:authorized => false, :the_request => 'the request',
|
|
47
|
+
:connection => @connection, :remote_host => '127.0.0.1' )
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
describe "included by an Applet" do
|
|
52
|
+
|
|
53
|
+
describe " that doesn't override any of the auth methods" do
|
|
54
|
+
before( :all ) do
|
|
55
|
+
@appletclass = Class.new( Arrow::Applet ) do
|
|
56
|
+
include Arrow::AppletAuthentication
|
|
57
|
+
|
|
58
|
+
applet_name "Hi, I'm a fixture applet!"
|
|
59
|
+
|
|
60
|
+
def initialize( *args )
|
|
61
|
+
@authenticated = false
|
|
62
|
+
@authorized = false
|
|
63
|
+
super
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
attr_reader :authenticated, :authorized
|
|
67
|
+
|
|
68
|
+
def authenticated_action( txn )
|
|
69
|
+
with_authentication( txn ) do |user|
|
|
70
|
+
@authenticated = true
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def authorized_action( txn )
|
|
75
|
+
with_authorization( txn ) do
|
|
76
|
+
@authorized = true
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
before( :each ) do
|
|
83
|
+
@applet = @appletclass.new( nil, nil, nil )
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "returns an UNAUTHORIZED response for an action wrapped in authentication" do
|
|
87
|
+
@txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
|
|
88
|
+
@applet.authenticated_action( @txn ).should =~ /requires auth/i
|
|
89
|
+
@applet.authenticated.should be_false()
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
it "returns an UNAUTHORIZED response for an action wrapped in authorization" do
|
|
93
|
+
@txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
|
|
94
|
+
@applet.authorized_action( @txn ).should =~ /requires auth/i
|
|
95
|
+
@applet.authorized.should be_false()
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
describe " that provides an implementation of #get_authenticated_user" do
|
|
101
|
+
before( :all ) do
|
|
102
|
+
@appletclass = Class.new( Arrow::Applet ) do
|
|
103
|
+
include Arrow::AppletAuthentication
|
|
104
|
+
|
|
105
|
+
applet_name "Hi, I'm a fixture applet!"
|
|
106
|
+
|
|
107
|
+
def initialize( *args )
|
|
108
|
+
@authenticated = false
|
|
109
|
+
@authorized = false
|
|
110
|
+
super
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
attr_reader :authenticated, :authorized
|
|
114
|
+
|
|
115
|
+
def authenticated_action( txn )
|
|
116
|
+
with_authentication( txn ) do |user|
|
|
117
|
+
@authenticated = true
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def authorized_action( txn )
|
|
122
|
+
with_authorization( txn ) do
|
|
123
|
+
@authorized = true
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def get_authenticated_user( txn )
|
|
128
|
+
return txn.user
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
before( :each ) do
|
|
134
|
+
@applet = @appletclass.new( nil, nil, nil )
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
it "returns an UNAUTHORIZED response for an action wrapped in authentication if " +
|
|
138
|
+
"the transaction doesn't contain a user" do
|
|
139
|
+
@txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
|
|
140
|
+
@applet.authenticated_action( @txn ).should =~ /requires auth/i
|
|
141
|
+
@applet.authenticated.should be_false()
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
it "returns a normal response for an action wrapped in authentication if " +
|
|
145
|
+
"the transaction does contain a user" do
|
|
146
|
+
@txn.stub!( :user ).and_return( :barney_the_clown )
|
|
147
|
+
@applet.authenticated_action( @txn ).should == true
|
|
148
|
+
@applet.authenticated.should be_true()
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
it "returns an UNAUTHORIZED response for an action wrapped in authorization if " +
|
|
152
|
+
"the transaction doesn't contain a user" do
|
|
153
|
+
@txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
|
|
154
|
+
@applet.authenticated_action( @txn ).should =~ /requires auth/i
|
|
155
|
+
@applet.authorized.should be_false()
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it "returns a FORBIDDEN response for an action wrapped in authorization if " +
|
|
159
|
+
"the transaction does contain a user" do
|
|
160
|
+
@txn.stub!( :user ).and_return( :blinky_the_wombat )
|
|
161
|
+
@txn.should_receive( :status= ).with( Apache::FORBIDDEN )
|
|
162
|
+
@applet.authorized_action( @txn ).should =~ /access denied/i
|
|
163
|
+
@applet.authorized.should be_false()
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
describe " that also provides an implementation of #user_is_authorized" do
|
|
169
|
+
before( :all ) do
|
|
170
|
+
@appletclass = Class.new( Arrow::Applet ) do
|
|
171
|
+
include Arrow::AppletAuthentication
|
|
172
|
+
|
|
173
|
+
applet_name "Hi, I'm a fixture applet!"
|
|
174
|
+
|
|
175
|
+
def initialize( *args )
|
|
176
|
+
@authenticated = false
|
|
177
|
+
@authorized = false
|
|
178
|
+
super
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
attr_reader :authenticated, :authorized
|
|
182
|
+
|
|
183
|
+
def authenticated_action( txn )
|
|
184
|
+
with_authentication( txn ) do |user|
|
|
185
|
+
@authenticated = true
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def authorized_action( txn )
|
|
190
|
+
with_authorization( txn ) do
|
|
191
|
+
@authorized = true
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def get_authenticated_user( txn )
|
|
196
|
+
# Simplified for testing -- lets the test set whether or not authentication
|
|
197
|
+
# exists
|
|
198
|
+
return txn.user
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
def user_is_authorized( user, txn )
|
|
202
|
+
# Simplified for testing -- lets the test set whether or not authorization
|
|
203
|
+
# exists
|
|
204
|
+
return txn.authorized
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
before( :each ) do
|
|
210
|
+
@applet = @appletclass.new( nil, nil, nil )
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
it "returns an UNAUTHORIZED response for an action wrapped in authentication if " +
|
|
214
|
+
"the transaction doesn't contain a user" do
|
|
215
|
+
@txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
|
|
216
|
+
@applet.authenticated_action( @txn ).should =~ /requires auth/i
|
|
217
|
+
@applet.authenticated.should be_false()
|
|
218
|
+
@applet.authorized.should be_false()
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
it "returns a FORBIDDEN response for an action wrapped in authentication if " +
|
|
222
|
+
"the transaction does contain a user" do
|
|
223
|
+
@txn.stub!( :user ).and_return( :barney_the_clown )
|
|
224
|
+
@applet.authenticated_action( @txn ).should == true
|
|
225
|
+
@applet.authenticated.should be_true()
|
|
226
|
+
@applet.authorized.should be_false()
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
it "returns an UNAUTHORIZED response for an action wrapped in authorization if " +
|
|
230
|
+
"the transaction doesn't contain a user" do
|
|
231
|
+
@txn.should_receive( :status= ).with( Apache::HTTP_UNAUTHORIZED )
|
|
232
|
+
@applet.authenticated_action( @txn ).should =~ /requires auth/i
|
|
233
|
+
@applet.authorized.should be_false()
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
it "returns a FORBIDDEN response for an action wrapped in authorization if " +
|
|
237
|
+
"the transaction does contain a user, but the user isn't authorized" do
|
|
238
|
+
@txn.stub!( :user ).and_return( :gurney_halleck )
|
|
239
|
+
@txn.should_receive( :status= ).with( Apache::FORBIDDEN )
|
|
240
|
+
@applet.authorized_action( @txn ).should =~ /access denied/i
|
|
241
|
+
@applet.authorized.should be_false()
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
it "returns a normal response for an action wrapped in authorization if " +
|
|
245
|
+
"the transaction does contain a user, and the user is authorized" do
|
|
246
|
+
@txn.stub!( :user ).and_return( :alia_atreides )
|
|
247
|
+
@txn.stub!( :authorized ).and_return( true )
|
|
248
|
+
@applet.authorized_action( @txn ).should == true
|
|
249
|
+
@applet.authorized.should be_true()
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
describe Arrow::AccessControls do
|
|
259
|
+
include Arrow::SpecHelpers,
|
|
260
|
+
Arrow::AppletMatchers
|
|
261
|
+
|
|
262
|
+
before( :all ) do
|
|
263
|
+
setup_logging( :crit )
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
before( :each ) do
|
|
267
|
+
@uri = '/testing'
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
after( :all ) do
|
|
271
|
+
reset_logging()
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
it "adds a declarative method to including applet classes for adding to the list" +
|
|
276
|
+
" of methods which can be run without authentication" do
|
|
277
|
+
@applet_class = Class.new( Arrow::Applet ) do
|
|
278
|
+
include Arrow::AccessControls
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
@applet_class.respond_to?( :unauthenticated_actions )
|
|
282
|
+
@applet_class.unauthenticated_actions.should have(3).members
|
|
283
|
+
@applet_class.unauthenticated_actions.should include( :login, :logout, :deny_access )
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
describe "included in an Applet" do
|
|
288
|
+
|
|
289
|
+
describe " that doesn't declare any other unauthenticated actions" do
|
|
290
|
+
before( :all ) do
|
|
291
|
+
@applet_class = Class.new( Arrow::Applet ) do
|
|
292
|
+
include Arrow::AccessControls
|
|
293
|
+
|
|
294
|
+
def action_missing_action( txn, action, *args )
|
|
295
|
+
return action
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
def login_action( txn, *args )
|
|
299
|
+
return :login
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
def logout_action( txn, *args )
|
|
303
|
+
return :logout
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
def deny_access_action( txn, *args )
|
|
307
|
+
return :deny_access
|
|
308
|
+
end
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
before( :each ) do
|
|
313
|
+
@applet = @applet_class.new( nil, nil, @uri )
|
|
314
|
+
@fakerequest = Apache::Request.new( @uri )
|
|
315
|
+
@txn = Arrow::Transaction.new( @fakerequest, nil, nil )
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
|
|
319
|
+
it "doesn't require authentication for the :login action" do
|
|
320
|
+
@applet.run( @txn, :login ).should == :login
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
it "doesn't require authentication for the :logout action" do
|
|
324
|
+
@applet.run( @txn, :logout ).should == :logout
|
|
325
|
+
end
|
|
326
|
+
|
|
327
|
+
it "doesn't require authentication for the :deny_access action" do
|
|
328
|
+
@applet.run( @txn, :deny_access ).should == :deny_access
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
it "requires authentication for any other action" do
|
|
332
|
+
@applet.run( @txn, :serenity ).should == :login
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
end
|
|
336
|
+
|
|
337
|
+
describe " that declares a custom unauthenticated action" do
|
|
338
|
+
before( :all ) do
|
|
339
|
+
@applet_class = Class.new( Arrow::Applet ) do
|
|
340
|
+
include Arrow::AccessControls
|
|
341
|
+
|
|
342
|
+
unauthenticated_actions :willy_nilly, :action_missing
|
|
343
|
+
|
|
344
|
+
def action_missing_action( txn, action, *args )
|
|
345
|
+
return :action_missing
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
def willy_nilly_action( txn, *args )
|
|
349
|
+
return :willy_nilly
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
def serenity_action( txn, *args )
|
|
353
|
+
return :serenity
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
def login_action( txn, *args )
|
|
357
|
+
return :login
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def logout_action( txn, *args )
|
|
361
|
+
return :logout
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def deny_access_action( txn, *args )
|
|
365
|
+
return :deny_access
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
before( :each ) do
|
|
371
|
+
@applet = @applet_class.new( nil, nil, @uri )
|
|
372
|
+
@fakerequest = Apache::Request.new( @uri )
|
|
373
|
+
@txn = Arrow::Transaction.new( @fakerequest, nil, nil )
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
it "doesn't require authentication for the :login action" do
|
|
378
|
+
@applet.run( @txn, :login ).should == :login
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
it "doesn't require authentication for the :logout action" do
|
|
382
|
+
@applet.run( @txn, :logout ).should == :logout
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
it "doesn't require authentication for the :deny_access action" do
|
|
386
|
+
@applet.run( @txn, :deny_access ).should == :deny_access
|
|
387
|
+
end
|
|
388
|
+
|
|
389
|
+
it "doesn't require authentication for actions declared as unauthenticated" do
|
|
390
|
+
@applet.run( @txn, :willy_nilly ).should == :willy_nilly
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
it "doesn't require authentication for remapped actions declared as unauthenticated" do
|
|
394
|
+
@applet.run( @txn, :klang_locke ).should == :action_missing
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
it "requires authentication for any other action" do
|
|
398
|
+
@applet.run( @txn, :serenity ).should == :login
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
end # describe "Applet mixins"
|
|
407
|
+
|
|
408
|
+
|
|
409
|
+
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# Specification for the Arrow::Applet class
|
|
4
|
+
# $Id$
|
|
5
|
+
#
|
|
6
|
+
# Copyright (c) 2004-2008 The FaerieMUD Consortium. Most rights reserved.
|
|
7
|
+
#
|
|
8
|
+
|
|
9
|
+
BEGIN {
|
|
10
|
+
require 'pathname'
|
|
11
|
+
basedir = Pathname.new( __FILE__ ).dirname.parent.parent
|
|
12
|
+
|
|
13
|
+
libdir = basedir + "lib"
|
|
14
|
+
|
|
15
|
+
$LOAD_PATH.unshift( libdir ) unless $LOAD_PATH.include?( libdir )
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
require 'spec'
|
|
20
|
+
require 'apache/fakerequest'
|
|
21
|
+
require 'arrow'
|
|
22
|
+
require 'arrow/applet'
|
|
23
|
+
require 'arrow/appletregistry'
|
|
24
|
+
require 'arrow/config'
|
|
25
|
+
require 'spec/lib/helpers'
|
|
26
|
+
rescue LoadError
|
|
27
|
+
unless Object.const_defined?( :Gem )
|
|
28
|
+
require 'rubygems'
|
|
29
|
+
retry
|
|
30
|
+
end
|
|
31
|
+
raise
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
#####################################################################
|
|
36
|
+
### C O N T E X T S
|
|
37
|
+
#####################################################################
|
|
38
|
+
|
|
39
|
+
describe Arrow::AppletRegistry do
|
|
40
|
+
include Arrow::SpecHelpers
|
|
41
|
+
|
|
42
|
+
APPLETREGISTRY_TEST_CONFIG = {
|
|
43
|
+
:applets => {
|
|
44
|
+
:path => Arrow::Path.new( "applets:specs/data/applets" ),
|
|
45
|
+
:pattern => '*.rb',
|
|
46
|
+
:pollInterval => 5,
|
|
47
|
+
:missingApplet => '/missing',
|
|
48
|
+
:errorApplet => '/error',
|
|
49
|
+
|
|
50
|
+
:layout => {
|
|
51
|
+
"/" => "Setup",
|
|
52
|
+
"/missing" => "NoSuchAppletHandler",
|
|
53
|
+
"/error" => "ErrorHandler",
|
|
54
|
+
"/status" => "ServerStatus",
|
|
55
|
+
"/hello" => "Hello",
|
|
56
|
+
"/args" => "ArgumentTester",
|
|
57
|
+
"/protected" => "ProtectedDelegator",
|
|
58
|
+
"/protected/hello" => "Hello",
|
|
59
|
+
"/counted" => "AccessCounter",
|
|
60
|
+
"/counted/hello" => "Hello",
|
|
61
|
+
|
|
62
|
+
"/test" => "TestApplet",
|
|
63
|
+
"/foo" => "BargleApplet",
|
|
64
|
+
},
|
|
65
|
+
:config => {},
|
|
66
|
+
},
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
GEM_CONFIG = APPLETREGISTRY_TEST_CONFIG.merge({
|
|
70
|
+
:gems => {
|
|
71
|
+
:require_signed => false,
|
|
72
|
+
:autoinstall => false,
|
|
73
|
+
:path => Arrow::Path.new([ "gems", *Gem.path ]),
|
|
74
|
+
:applets => {
|
|
75
|
+
'arrow-demo-apps' => '>= 0.0.3',
|
|
76
|
+
'arrow-management-apps' => '= 0.9.4',
|
|
77
|
+
'arrow-laikapedia' => nil,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
### Set up a stubbed applet instance that can be loaded via
|
|
84
|
+
### Arrow::Applet.load.
|
|
85
|
+
def fixture_appletclass( path, name, classname )
|
|
86
|
+
applet = stub( "#{classname} instance (path)" )
|
|
87
|
+
appletclass = stub( "#{classname} class",
|
|
88
|
+
:name => name,
|
|
89
|
+
:normalized_name => classname,
|
|
90
|
+
:new => applet
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
Arrow::Applet.should_receive( :load ).with( path ).once.
|
|
94
|
+
and_return([ appletclass ])
|
|
95
|
+
File.should_receive( :mtime ).with( path ).and_return( Time.now )
|
|
96
|
+
|
|
97
|
+
return applet, appletclass
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
before( :all ) do
|
|
103
|
+
setup_logging( :crit )
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
after( :all ) do
|
|
107
|
+
reset_logging()
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
before( :each ) do
|
|
112
|
+
Arrow::Applet.stub!( :load ).and_return( [] )
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
it "ignores the gem home if it isn't a directory" do
|
|
117
|
+
homepath = mock( 'Pathname object for gem home' )
|
|
118
|
+
|
|
119
|
+
Gem.should_receive( :user_home ).and_return( :user_home )
|
|
120
|
+
Pathname.should_receive( :new ).with( :user_home ).and_return( homepath )
|
|
121
|
+
homepath.should_receive( :+ ).with( 'gems' ).and_return( homepath )
|
|
122
|
+
homepath.should_receive( :directory? ).and_return( false )
|
|
123
|
+
|
|
124
|
+
Arrow::AppletRegistry.get_safe_gemhome.should be_nil()
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
it "ignores the gem home if it's world-writable" do
|
|
129
|
+
homepath = mock( 'Pathname object for gem home' )
|
|
130
|
+
homepath_stat = mock( 'Stat data for the gem home' )
|
|
131
|
+
|
|
132
|
+
Gem.should_receive( :user_home ).and_return( :user_home )
|
|
133
|
+
Pathname.should_receive( :new ).with( :user_home ).and_return( homepath )
|
|
134
|
+
homepath.should_receive( :+ ).with( 'gems' ).and_return( homepath )
|
|
135
|
+
homepath.should_receive( :directory? ).and_return( true )
|
|
136
|
+
homepath.should_receive( :stat ).and_return( homepath_stat )
|
|
137
|
+
|
|
138
|
+
homepath_stat.should_receive( :mode ).and_return( 0777 )
|
|
139
|
+
|
|
140
|
+
Arrow::AppletRegistry.get_safe_gemhome.should be_nil()
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
it "returns an untainted Pathname for the gem home if it's sane" do
|
|
145
|
+
homepath = mock( 'Pathname object for gem home' )
|
|
146
|
+
homepath.taint
|
|
147
|
+
homepath_stat = mock( 'Stat data for the gem home' )
|
|
148
|
+
|
|
149
|
+
Gem.should_receive( :user_home ).and_return( :user_home )
|
|
150
|
+
Pathname.should_receive( :new ).with( :user_home ).and_return( homepath )
|
|
151
|
+
homepath.should_receive( :+ ).with( 'gems' ).and_return( homepath )
|
|
152
|
+
homepath.should_receive( :directory? ).and_return( true )
|
|
153
|
+
homepath.should_receive( :stat ).and_return( homepath_stat )
|
|
154
|
+
|
|
155
|
+
homepath_stat.should_receive( :mode ).and_return( 0755 )
|
|
156
|
+
|
|
157
|
+
rval = Arrow::AppletRegistry.get_safe_gemhome
|
|
158
|
+
|
|
159
|
+
rval.should == homepath
|
|
160
|
+
rval.should_not be_tainted()
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
it "loads any configured gems when it is created, and adds their template/ and applet/ " +
|
|
165
|
+
"directories to the template factory and path" do
|
|
166
|
+
Gem.should_receive( :activate ).with( 'arrow-demo-apps', '>= 0.0.3' ).
|
|
167
|
+
and_return( true )
|
|
168
|
+
Gem.should_receive( :datadir ).with( 'arrow-demo-apps' ).
|
|
169
|
+
and_return( '/some/path/arrow-demo-apps/data' )
|
|
170
|
+
Gem.should_receive( :activate ).with( 'arrow-management-apps', '= 0.9.4' ).
|
|
171
|
+
and_return( true )
|
|
172
|
+
Gem.should_receive( :datadir ).with( 'arrow-management-apps' ).
|
|
173
|
+
and_return( '/some/path/arrow-management-apps/data' )
|
|
174
|
+
Gem.should_receive( :activate ).with( 'arrow-laikapedia', Gem::Requirement.default ).
|
|
175
|
+
and_return( true )
|
|
176
|
+
Gem.should_receive( :datadir ).with( 'arrow-laikapedia' ).
|
|
177
|
+
and_return( '/some/path/arrow-laikapedia/data' )
|
|
178
|
+
|
|
179
|
+
config = Arrow::Config.new( GEM_CONFIG )
|
|
180
|
+
registry = Arrow::AppletRegistry.new( config )
|
|
181
|
+
|
|
182
|
+
registry.template_factory.path.should have(5).members
|
|
183
|
+
registry.template_factory.path.dirs.should include(
|
|
184
|
+
'/some/path/arrow-demo-apps/data/templates',
|
|
185
|
+
'/some/path/arrow-management-apps/data/templates',
|
|
186
|
+
'/some/path/arrow-laikapedia/data/templates'
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
registry.path.should have(5).members
|
|
190
|
+
registry.path.dirs.should include(
|
|
191
|
+
'/some/path/arrow-demo-apps/data/applets',
|
|
192
|
+
'/some/path/arrow-management-apps/data/applets',
|
|
193
|
+
'/some/path/arrow-laikapedia/data/applets'
|
|
194
|
+
)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
it "searches for applets in its list of paths, and loads all of them" do
|
|
199
|
+
test_applet, test_appletclass = fixture_appletclass( 'test.rb', 'test', 'TestApplet' )
|
|
200
|
+
bargle_applet, bargle_appletclass = fixture_appletclass( 'bargle.rb', 'bargle', 'BargleApplet' )
|
|
201
|
+
|
|
202
|
+
File.stub!( :exist? ).and_return( true )
|
|
203
|
+
|
|
204
|
+
Dir.should_receive( :[] ).with( 'applets/*.rb' ).and_return([ 'test.rb', 'bargle.rb' ])
|
|
205
|
+
|
|
206
|
+
applets_path = stub( 'applets path object', :dirs => ['applets'] )
|
|
207
|
+
applets_path.stub!( :each ).and_yield( 'applets' )
|
|
208
|
+
config = Arrow::Config.new( APPLETREGISTRY_TEST_CONFIG )
|
|
209
|
+
config.applets.path = applets_path
|
|
210
|
+
registry = Arrow::AppletRegistry.new( config )
|
|
211
|
+
|
|
212
|
+
registry.urispace.should have(2).members
|
|
213
|
+
registry.urispace.values.should include( bargle_applet, test_applet )
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
describe "instance" do
|
|
218
|
+
|
|
219
|
+
before( :all ) do
|
|
220
|
+
setup_logging( :crit )
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
TEST_URISPACE = {
|
|
224
|
+
"" => "Setup",
|
|
225
|
+
"hello" => "Hello",
|
|
226
|
+
"args" => "ArgumentTester",
|
|
227
|
+
"protected" => "ProtectedDelegator",
|
|
228
|
+
"protected/hello" => "Hello",
|
|
229
|
+
"counted" => "AccessCounter",
|
|
230
|
+
"counted/hello" => "Hello",
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
before( :each ) do
|
|
234
|
+
@config = Arrow::Config.new( APPLETREGISTRY_TEST_CONFIG )
|
|
235
|
+
@registry = Arrow::AppletRegistry.new( @config )
|
|
236
|
+
@registry.instance_variable_set( :@urispace, TEST_URISPACE )
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
it "can create an applet chain for a uri" do
|
|
241
|
+
@registry.find_applet_chain( '/protected/hello' ).should have(3).members
|
|
242
|
+
@registry.find_applet_chain( '/protected/hello' ).
|
|
243
|
+
collect {|cl| cl.path }.should == [ '', 'protected', 'protected/hello' ]
|
|
244
|
+
@registry.find_applet_chain( '/protected/hello' ).
|
|
245
|
+
collect {|cl| cl.applet }.should == [ 'Setup', 'ProtectedDelegator', 'Hello' ]
|
|
246
|
+
|
|
247
|
+
@registry.find_applet_chain( '/protected' ).should have(2).members
|
|
248
|
+
@registry.find_applet_chain( '/protected' ).
|
|
249
|
+
collect {|cl| cl.path }.should == [ '', 'protected' ]
|
|
250
|
+
@registry.find_applet_chain( '/protected' ).
|
|
251
|
+
collect {|cl| cl.applet }.should == [ 'Setup', 'ProtectedDelegator' ]
|
|
252
|
+
|
|
253
|
+
@registry.find_applet_chain( '/' ).should have(1).members
|
|
254
|
+
@registry.find_applet_chain( '/' ).
|
|
255
|
+
collect {|cl| cl.path }.should == [ '' ]
|
|
256
|
+
@registry.find_applet_chain( '/' ).
|
|
257
|
+
collect {|cl| cl.applet }.should == [ 'Setup' ]
|
|
258
|
+
|
|
259
|
+
@registry.find_applet_chain( '/args' ).should have(2).members
|
|
260
|
+
@registry.find_applet_chain( '/args' ).
|
|
261
|
+
collect {|cl| cl.path }.should == [ '', 'args' ]
|
|
262
|
+
@registry.find_applet_chain( '/args' ).
|
|
263
|
+
collect {|cl| cl.applet }.should == [ 'Setup', 'ArgumentTester' ]
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
it "reloads its applets if the configured interval has passed since applets " +
|
|
268
|
+
"were loaded" do
|
|
269
|
+
# Make sure the load time is far enough in the past
|
|
270
|
+
@registry.load_time = Time.now - 2000
|
|
271
|
+
@registry.should_receive( :reload_applets ).once
|
|
272
|
+
@registry.check_for_updates
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
it "doesn't reload its applets if the configured interval hasn't passed since " +
|
|
276
|
+
"applets were loaded" do
|
|
277
|
+
# Make sure the load time is far enough in the past
|
|
278
|
+
@registry.load_time = Time.now
|
|
279
|
+
@registry.should_not_receive( :reload_applets )
|
|
280
|
+
@registry.check_for_updates
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
it "doesn't reload its applets if reloading is turned off (interval is zero)" do
|
|
284
|
+
# Make sure the load time is far enough in the past
|
|
285
|
+
@config.applets.pollInterval = 0
|
|
286
|
+
@registry.load_time = Time.now - 2000
|
|
287
|
+
@registry.should_not_receive( :reload_applets )
|
|
288
|
+
@registry.check_for_updates
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
end
|
|
294
|
+
|