rubycas-server 0.4.2 → 0.5.0
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.txt +37 -0
- data/Manifest.txt +10 -0
- data/Rakefile +3 -2
- data/bin/rubycas-server-ctl +1 -1
- data/config.example.yml +44 -11
- data/custom_views.example.rb +11 -0
- data/lib/casserver/authenticators/ldap.rb +11 -5
- data/lib/casserver/conf.rb +35 -12
- data/lib/casserver/controllers.rb +111 -23
- data/lib/casserver/models.rb +23 -0
- data/lib/casserver/postambles.rb +21 -24
- data/lib/casserver/utils.rb +16 -0
- data/lib/casserver/version.rb +3 -3
- data/lib/casserver/views.rb +50 -36
- data/lib/casserver.rb +9 -2
- data/lib/themes/cas.css +1 -0
- data/vendor/camping-1.5.180/lib/camping/db.rb +78 -0
- data/vendor/camping-1.5.180/lib/camping/fastcgi.rb +244 -0
- data/vendor/camping-1.5.180/lib/camping/reloader.rb +163 -0
- data/vendor/camping-1.5.180/lib/camping/session.rb +123 -0
- data/vendor/camping-1.5.180/lib/camping/webrick.rb +65 -0
- data/vendor/camping-1.5.180/lib/camping-unabridged.rb +762 -0
- data/vendor/camping-1.5.180/lib/camping.rb +55 -0
- metadata +10 -11
@@ -0,0 +1,163 @@
|
|
1
|
+
module Camping
|
2
|
+
# == The Camping Reloader
|
3
|
+
#
|
4
|
+
# Camping apps are generally small and predictable. Many Camping apps are
|
5
|
+
# contained within a single file. Larger apps are split into a handful of
|
6
|
+
# other Ruby libraries within the same directory.
|
7
|
+
#
|
8
|
+
# Since Camping apps (and their dependencies) are loaded with Ruby's require
|
9
|
+
# method, there is a record of them in $LOADED_FEATURES. Which leaves a
|
10
|
+
# perfect space for this class to manage auto-reloading an app if any of its
|
11
|
+
# immediate dependencies changes.
|
12
|
+
#
|
13
|
+
# == Wrapping Your Apps
|
14
|
+
#
|
15
|
+
# Since bin/camping and the Camping::FastCGI class already use the Reloader,
|
16
|
+
# you probably don't need to hack it on your own. But, if you're rolling your
|
17
|
+
# own situation, here's how.
|
18
|
+
#
|
19
|
+
# Rather than this:
|
20
|
+
#
|
21
|
+
# require 'yourapp'
|
22
|
+
#
|
23
|
+
# Use this:
|
24
|
+
#
|
25
|
+
# require 'camping/reloader'
|
26
|
+
# Camping::Reloader.new('/path/to/yourapp.rb')
|
27
|
+
#
|
28
|
+
# The reloader will take care of requiring the app and monitoring all files
|
29
|
+
# for alterations.
|
30
|
+
class Reloader
|
31
|
+
attr_accessor :klass, :mtime, :mount, :requires
|
32
|
+
|
33
|
+
# Creates the reloader, assigns a +script+ to it and initially loads the
|
34
|
+
# application. Pass in the full path to the script, otherwise the script
|
35
|
+
# will be loaded relative to the current working directory.
|
36
|
+
def initialize(script)
|
37
|
+
@script = File.expand_path(script)
|
38
|
+
@mount = File.basename(script, '.rb')
|
39
|
+
@requires = nil
|
40
|
+
load_app
|
41
|
+
end
|
42
|
+
|
43
|
+
# Find the application, based on the script name.
|
44
|
+
def find_app(title)
|
45
|
+
@klass = Object.const_get(Object.constants.grep(/^#{title}$/i)[0]) rescue nil
|
46
|
+
end
|
47
|
+
|
48
|
+
# If the file isn't found, if we need to remove the app from the global
|
49
|
+
# namespace, this will be sure to do so and set @klass to nil.
|
50
|
+
def remove_app
|
51
|
+
Object.send :remove_const, @klass.name if @klass
|
52
|
+
@klass = nil
|
53
|
+
end
|
54
|
+
|
55
|
+
# Loads (or reloads) the application. The reloader will take care of calling
|
56
|
+
# this for you. You can certainly call it yourself if you feel it's warranted.
|
57
|
+
def load_app
|
58
|
+
title = File.basename(@script)[/^([\w_]+)/,1].gsub /_/,''
|
59
|
+
begin
|
60
|
+
all_requires = $LOADED_FEATURES.dup
|
61
|
+
load @script
|
62
|
+
@requires = ($LOADED_FEATURES - all_requires).select do |req|
|
63
|
+
req.index(File.basename(@script) + "/") == 0 || req.index(title + "/") == 0
|
64
|
+
end
|
65
|
+
rescue Exception => e
|
66
|
+
puts "!! trouble loading #{title}: [#{e.class}] #{e.message}"
|
67
|
+
puts e.backtrace.join("\n")
|
68
|
+
find_app title
|
69
|
+
remove_app
|
70
|
+
return
|
71
|
+
end
|
72
|
+
|
73
|
+
@mtime = mtime
|
74
|
+
find_app title
|
75
|
+
unless @klass and @klass.const_defined? :C
|
76
|
+
puts "!! trouble loading #{title}: not a Camping app, no #{title.capitalize} module found"
|
77
|
+
remove_app
|
78
|
+
return
|
79
|
+
end
|
80
|
+
|
81
|
+
Reloader.conditional_connect
|
82
|
+
@klass.create if @klass.respond_to? :create
|
83
|
+
@klass
|
84
|
+
end
|
85
|
+
|
86
|
+
# The timestamp of the most recently modified app dependency.
|
87
|
+
def mtime
|
88
|
+
((@requires || []) + [@script]).map do |fname|
|
89
|
+
fname = fname.gsub(/^#{Regexp::quote File.dirname(@script)}\//, '')
|
90
|
+
begin
|
91
|
+
File.mtime(File.join(File.dirname(@script), fname))
|
92
|
+
rescue Errno::ENOENT
|
93
|
+
remove_app
|
94
|
+
@mtime
|
95
|
+
end
|
96
|
+
end.max
|
97
|
+
end
|
98
|
+
|
99
|
+
# Conditional reloading of the app. This gets called on each request and
|
100
|
+
# only reloads if the modification times on any of the files is updated.
|
101
|
+
def reload_app
|
102
|
+
return if @klass and @mtime and mtime <= @mtime
|
103
|
+
|
104
|
+
if @requires
|
105
|
+
@requires.each { |req| $LOADED_FEATURES.delete(req) }
|
106
|
+
end
|
107
|
+
k = @klass
|
108
|
+
Object.send :remove_const, k.name if k
|
109
|
+
load_app
|
110
|
+
end
|
111
|
+
|
112
|
+
# Conditionally reloads (using reload_app.) Then passes the request through
|
113
|
+
# to the wrapped Camping app.
|
114
|
+
def run(*a)
|
115
|
+
reload_app
|
116
|
+
if @klass
|
117
|
+
@klass.run(*a)
|
118
|
+
else
|
119
|
+
Camping.run(*a)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns source code for the main script in the application.
|
124
|
+
def view_source
|
125
|
+
File.read(@script)
|
126
|
+
end
|
127
|
+
|
128
|
+
class << self
|
129
|
+
def database=(db)
|
130
|
+
@database = db
|
131
|
+
end
|
132
|
+
def log=(log)
|
133
|
+
@log = log
|
134
|
+
end
|
135
|
+
def conditional_connect
|
136
|
+
# If database models are present, `autoload?` will return nil.
|
137
|
+
unless Camping::Models.autoload? :Base
|
138
|
+
require 'logger'
|
139
|
+
require 'camping/session'
|
140
|
+
Camping::Models::Base.establish_connection @database if @database
|
141
|
+
|
142
|
+
case @log
|
143
|
+
when Logger
|
144
|
+
Camping::Models::Base.logger = @log
|
145
|
+
when String
|
146
|
+
Camping::Models::Base.logger = Logger.new(@log == "-" ? STDOUT : @log)
|
147
|
+
end
|
148
|
+
|
149
|
+
Camping::Models::Session.create_schema
|
150
|
+
|
151
|
+
if @database and @database[:adapter] == 'sqlite3'
|
152
|
+
begin
|
153
|
+
require 'sqlite3_api'
|
154
|
+
rescue LoadError
|
155
|
+
puts "!! Your SQLite3 adapter isn't a compiled extension."
|
156
|
+
abort "!! Please check out http://code.whytheluckystiff.net/camping/wiki/BeAlertWhenOnSqlite3 for tips."
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# == About camping/session.rb
|
2
|
+
#
|
3
|
+
# This file contains two modules which supply basic sessioning to your Camping app.
|
4
|
+
# Again, we're dealing with a pretty little bit of code: approx. 60 lines.
|
5
|
+
#
|
6
|
+
# * Camping::Models::Session is a module which adds a single <tt>sessions</tt> table
|
7
|
+
# to your database.
|
8
|
+
# * Camping::Session is a module which you will mix into your application (or into
|
9
|
+
# specific controllers which require sessions) to supply a <tt>@state</tt> variable
|
10
|
+
# you can use in controllers and views.
|
11
|
+
#
|
12
|
+
# For a basic tutorial, see the *Getting Started* section of the Camping::Session module.
|
13
|
+
require 'camping'
|
14
|
+
|
15
|
+
module Camping::Models
|
16
|
+
# A database table for storing Camping sessions. Contains a unique 32-character hashid, a
|
17
|
+
# creation timestamp, and a column of serialized data called <tt>ivars</tt>.
|
18
|
+
class Session < Base
|
19
|
+
serialize :ivars
|
20
|
+
def []=(k, v) # :nodoc:
|
21
|
+
self.ivars[k] = v
|
22
|
+
end
|
23
|
+
def [](k) # :nodoc:
|
24
|
+
self.ivars[k] rescue nil
|
25
|
+
end
|
26
|
+
|
27
|
+
RAND_CHARS = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']
|
28
|
+
|
29
|
+
# Generates a new session ID and creates a row for the new session in the database.
|
30
|
+
def self.generate cookies
|
31
|
+
rand_max = RAND_CHARS.size
|
32
|
+
sid = (0...32).inject("") { |ret,_| ret << RAND_CHARS[rand(rand_max)] }
|
33
|
+
sess = Session.create :hashid => sid, :ivars => Camping::H[]
|
34
|
+
cookies.camping_sid = sess.hashid
|
35
|
+
sess
|
36
|
+
end
|
37
|
+
|
38
|
+
# Gets the existing session based on the <tt>camping_sid</tt> available in cookies.
|
39
|
+
# If none is found, generates a new session.
|
40
|
+
def self.persist cookies
|
41
|
+
if cookies.camping_sid
|
42
|
+
session = Camping::Models::Session.find_by_hashid cookies.camping_sid
|
43
|
+
end
|
44
|
+
unless session
|
45
|
+
session = Camping::Models::Session.generate cookies
|
46
|
+
end
|
47
|
+
session
|
48
|
+
end
|
49
|
+
|
50
|
+
# Builds the session table in the database. To be used in your application's
|
51
|
+
# <tt>create</tt> method.
|
52
|
+
#
|
53
|
+
# Like so:
|
54
|
+
#
|
55
|
+
# def Blog.create
|
56
|
+
# Camping::Models::Session.create_schema
|
57
|
+
# unless Blog::Models::Post.table_exists?
|
58
|
+
# ActiveRecord::Schema.define(&Blog::Models.schema)
|
59
|
+
# end
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
def self.create_schema
|
63
|
+
unless table_exists?
|
64
|
+
ActiveRecord::Schema.define do
|
65
|
+
create_table :sessions, :force => true do |t|
|
66
|
+
t.column :id, :integer, :null => false
|
67
|
+
t.column :hashid, :string, :limit => 32
|
68
|
+
t.column :created_at, :datetime
|
69
|
+
t.column :ivars, :text
|
70
|
+
end
|
71
|
+
end
|
72
|
+
reset_column_information
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module Camping
|
79
|
+
# The Camping::Session module is designed to be mixed into your application or into specific
|
80
|
+
# controllers which require sessions. This module defines a <tt>service</tt> method which
|
81
|
+
# intercepts all requests handed to those controllers.
|
82
|
+
#
|
83
|
+
# == Getting Started
|
84
|
+
#
|
85
|
+
# To get sessions working for your application:
|
86
|
+
#
|
87
|
+
# 1. <tt>require 'camping/session'</tt>
|
88
|
+
# 2. Mixin the module: <tt>module YourApp; include Camping::Session end</tt>
|
89
|
+
# 3. In your application's <tt>create</tt> method, add a call to <tt>Camping::Models::Session.create_schema</tt>
|
90
|
+
# 4. Throughout your application, use the <tt>@state</tt> var like a hash to store your application's data.
|
91
|
+
#
|
92
|
+
# If you are unfamiliar with the <tt>create</tt> method, see
|
93
|
+
# http://code.whytheluckystiff.net/camping/wiki/GiveUsTheCreateMethod.
|
94
|
+
#
|
95
|
+
# == A Few Notes
|
96
|
+
#
|
97
|
+
# * The session ID is stored in a cookie. Look in <tt>@cookies.camping_sid</tt>.
|
98
|
+
# * The session data is stored in the <tt>sessions</tt> table in your database.
|
99
|
+
# * All mounted Camping apps using this class will use the same database table.
|
100
|
+
# * However, your application's data is stored in its own hash.
|
101
|
+
# * Session data is only saved if it has changed.
|
102
|
+
module Session
|
103
|
+
# This <tt>service</tt> method, when mixed into controllers, intercepts requests
|
104
|
+
# and wraps them with code to start and close the session. If a session isn't found
|
105
|
+
# in the database it is created. The <tt>@state</tt> variable is set and if it changes,
|
106
|
+
# it is saved back into the database.
|
107
|
+
def service(*a)
|
108
|
+
session = Camping::Models::Session.persist @cookies
|
109
|
+
app = self.class.name.gsub(/^(\w+)::.+$/, '\1')
|
110
|
+
@state = (session[app] ||= Camping::H[])
|
111
|
+
hash_before = Marshal.dump(@state).hash
|
112
|
+
s = super(*a)
|
113
|
+
if session
|
114
|
+
hash_after = Marshal.dump(@state).hash
|
115
|
+
unless hash_before == hash_after
|
116
|
+
session[app] = @state
|
117
|
+
session.save
|
118
|
+
end
|
119
|
+
end
|
120
|
+
s
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# == About camping/webrick.rb
|
2
|
+
#
|
3
|
+
# For many who have Ruby installed, Camping and WEBrick is a great option.
|
4
|
+
# It's definitely the easiest configuration, however some performance is sacrificed.
|
5
|
+
# For better speed, check out Mongrel at http://mongrel.rubyforge.org/, which comes
|
6
|
+
# with Camping hooks and is supported by the Camping Tool.
|
7
|
+
require 'camping'
|
8
|
+
require 'webrick/httpservlet/abstract.rb'
|
9
|
+
|
10
|
+
module WEBrick
|
11
|
+
# WEBrick::CampingHandler is a very simple handle for hosting Camping apps in
|
12
|
+
# a WEBrick server. It's used much like any other WEBrick handler.
|
13
|
+
#
|
14
|
+
# == Mounting a Camping App
|
15
|
+
#
|
16
|
+
# Assuming Camping.goes(:Blog), the Blog application can be mounted alongside
|
17
|
+
# other WEBrick mounts.
|
18
|
+
#
|
19
|
+
# s = WEBrick::HTTPServer.new(:BindAddress => host, :Port => port)
|
20
|
+
# s.mount "/blog", WEBrick::CampingHandler, Blog
|
21
|
+
# s.mount_proc("/") { ... }
|
22
|
+
#
|
23
|
+
# == How Does it Compare?
|
24
|
+
#
|
25
|
+
# Compared to other handlers, WEBrick is well-equipped in terms of features.
|
26
|
+
#
|
27
|
+
# * The <tt>X-Sendfile</tt> header is supported, along with etags and
|
28
|
+
# modification time headers for the file served. Since this handler
|
29
|
+
# is a subclass of WEBrick::HTTPServlet::DefaultFileHandler, all of its
|
30
|
+
# logic is used.
|
31
|
+
# * IO is streaming up and down. When you upload a file, it is streamed to
|
32
|
+
# the server's filesystem. When you download a file, it is streamed to
|
33
|
+
# your browser.
|
34
|
+
#
|
35
|
+
# While WEBrick is a bit slower than Mongrel and FastCGI options, it's
|
36
|
+
# a decent choice, for sure!
|
37
|
+
class CampingHandler < WEBrick::HTTPServlet::DefaultFileHandler
|
38
|
+
# Creates a CampingHandler, which answers for the application within +klass+.
|
39
|
+
def initialize(server, klass)
|
40
|
+
super(server, klass)
|
41
|
+
@klass = klass
|
42
|
+
end
|
43
|
+
# Handler for WEBrick requests (also aliased as do_POST).
|
44
|
+
def service(req, resp)
|
45
|
+
controller = @klass.run((req.body and StringIO.new(req.body)), req.meta_vars)
|
46
|
+
resp.status = controller.status
|
47
|
+
@local_path = nil
|
48
|
+
controller.headers.each do |k, v|
|
49
|
+
if k =~ /^X-SENDFILE$/i
|
50
|
+
@local_path = v
|
51
|
+
else
|
52
|
+
[*v].each do |vi|
|
53
|
+
resp[k] = vi
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
if @local_path
|
59
|
+
do_GET(req, resp)
|
60
|
+
else
|
61
|
+
resp.body = controller.body.to_s
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|