iauthu 0.0.1

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/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1
2
+
3
+ * Initial release
4
+
data/Manifest.txt ADDED
@@ -0,0 +1,15 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/iauthu
6
+ lib/iauthu.rb
7
+ lib/iauthu/authenticator/base.rb
8
+ lib/iauthu/authenticator/chained.rb
9
+ lib/iauthu/authenticator/ldap.rb
10
+ lib/iauthu/request.rb
11
+ lib/iauthu/server.rb
12
+ lib/iauthu/site.rb
13
+ lib/iauthu/version.rb
14
+ template/iauthu.conf.tmpl
15
+ test/test_iauthu.rb
data/README.txt ADDED
@@ -0,0 +1,68 @@
1
+ = IAuthU
2
+
3
+ * http://github.com/rheimbuch/iauthu
4
+
5
+ == DESCRIPTION:
6
+
7
+ IAuthU provides a basic iTunesU authentication server, along with libraries for building iTunesU authentication servers into your own application.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ * Features
12
+ * Support for iTunesU authentication system.
13
+ * Plug-able authentication back-ends.
14
+ * Includes authentication back-ends for basic LDAP and chained authentication sources.
15
+ * Supports running as CGI, FastCGI, WEBrick, Mongrel, etc using Rack
16
+
17
+ == SYNOPSIS:
18
+
19
+ * Generate the config file in /etc/iauthu/iauthu.conf (see `iauthu -h` for all options)
20
+
21
+ $ sudo iauthu -g
22
+
23
+ * Edit /etc/iauthu/iauth.conf and fill in your iTunesU authentication settings.
24
+
25
+ * To run as a standalone server:
26
+
27
+ $ iauthu
28
+
29
+ * To run as a cgi, symlink the iauthu binary into your cgi-bin directory or any directory that allows executing cgi scripts:
30
+
31
+ $ ln -s /usr/bin/iauthu /usr/lib/cgi-bin/iauthu.cgi
32
+
33
+
34
+ == REQUIREMENTS:
35
+
36
+ * Rack
37
+ * Markaby
38
+ * Ruby-HMAC
39
+ * Net/LDAP
40
+
41
+ == INSTALL:
42
+
43
+ gem install iauthu
44
+
45
+ == LICENSE:
46
+
47
+ (The MIT License)
48
+
49
+ Copyright (c) 2008 Ryan Heimbuch
50
+
51
+ Permission is hereby granted, free of charge, to any person obtaining
52
+ a copy of this software and associated documentation files (the
53
+ 'Software'), to deal in the Software without restriction, including
54
+ without limitation the rights to use, copy, modify, merge, publish,
55
+ distribute, sublicense, and/or sell copies of the Software, and to
56
+ permit persons to whom the Software is furnished to do so, subject to
57
+ the following conditions:
58
+
59
+ The above copyright notice and this permission notice shall be
60
+ included in all copies or substantial portions of the Software.
61
+
62
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
63
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
64
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
65
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
66
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
67
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
68
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'hoe'
5
+ require './lib/iauthu.rb'
6
+
7
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
8
+
9
+ Hoe.new('iauthu', IAuthU::VERSION) do |p|
10
+ p.rubyforge_name = 'iauthu' # if different than lowercase project name
11
+ p.developer('Ryan Heimbuch', 'rheimbuch@gmail.com')
12
+ p.remote_rdoc_dir = '' # Release to root
13
+ p.extra_deps << ['rack', '>=0.3.0']
14
+ p.extra_deps << ['mongrel', '>=1.1.5']
15
+ p.extra_deps << ['markaby', '>=0.5']
16
+ p.extra_deps << ['ruby-hmac', '>=0.3.1']
17
+ p.extra_deps << ['ruby-net-ldap', '>=0.0.4']
18
+ end
19
+
20
+ # vim: syntax=Ruby
data/bin/iauthu ADDED
@@ -0,0 +1,161 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
3
+ require 'rubygems'
4
+ require 'fileutils'
5
+ require 'optparse'
6
+ require 'ostruct'
7
+ require 'erb'
8
+ require 'iauthu'
9
+
10
+ GLOBAL_CONFIG = {:trace => false}
11
+
12
+
13
+ module IAuthU
14
+ module Command # :nodoc:
15
+
16
+ SYSTEM_CONFIG_FILE = "/etc/iauthu/iauthu.conf"
17
+
18
+ def self.call(args)
19
+ options = Options.new(args)
20
+
21
+ config_file = args.shift
22
+ config_file = SYSTEM_CONFIG_FILE unless config_file && !config_file.empty?
23
+
24
+
25
+ if options.opts["generate"]
26
+ # Generate a config file
27
+ self.generate_config(config_file, options.opts)
28
+ else
29
+ # Run the server
30
+ self.run_server(config_file, options.opts)
31
+ end
32
+
33
+
34
+ end
35
+
36
+ def self.run_server(config, options={})
37
+
38
+ if config && File.exist?(config)
39
+ file = File.read(config)
40
+ server = IAuthU::Server.build { eval(file) }
41
+ if ENV['REQUEST_METHOD'] # Detect if running as a cgi
42
+ server.runner = IAuthU::Server::Builder::RUNNERS[:cgi]
43
+ end
44
+ server.run
45
+ else
46
+ err = ""
47
+ raise "No config file is available.\n" \
48
+ "Specify a config file or run `iauthu -g [FILE]` to generate a config file."
49
+ end
50
+ end
51
+
52
+ def self.generate_config(config, options={})
53
+ if File.exist?(config) && !options["force"]
54
+ raise "Config file '#{config}' already exists: will not overwrite."
55
+ end
56
+ vars = OpenStruct.new options
57
+ template_dir = File.join(File.dirname(__FILE__), "..", "template")
58
+ template = File.read(File.join(template_dir, 'iauthu.conf.tmpl'))
59
+ result = ERB.new(template).result(vars.send(:binding))
60
+ unless File.exist?(File.dirname(config))
61
+ FileUtils.mkdir_p(File.dirname(config))
62
+ end
63
+ File.open(config, "w") do |f|
64
+ f.puts result
65
+ end
66
+ end
67
+
68
+
69
+ class Options # :nodoc:
70
+ def initialize(args)
71
+ @opts = {}
72
+ parser.parse!(args)
73
+ end
74
+
75
+ attr_reader :parser
76
+
77
+ def opts
78
+ @opts
79
+ end
80
+
81
+ def config
82
+ @opts
83
+ end
84
+
85
+ def banner
86
+ parser.to_s
87
+ end
88
+
89
+ def parser
90
+ @parser ||= OptionParser.new do |opts|
91
+ opts.banner = "Usage: iauthu [CONFIG_FILE]"
92
+ opts.separator ""
93
+ opts.separator "If [CONFIG_FILE] is absent, iauthu will look for ./iauthu.conf and /etc/iauthu/iauthu.conf"
94
+ opts.separator ""
95
+
96
+ opts.on("-g", "--generate", "Generate a config file.") do
97
+ config["generate"] = true
98
+ end
99
+
100
+ opts.on("--trace", "Enable error tracebacks and debugging output.") do |trace|
101
+ GLOBAL_CONFIG[:trace] = trace
102
+ end
103
+
104
+ opts.separator ""
105
+ opts.separator "The following options apply only to config file generation."
106
+ opts.separator ""
107
+
108
+ opts.on("-f", "--force", "Force generation & overwrite config file." ) do |force|
109
+ config["force"] = force
110
+ end
111
+
112
+ opts.on("--url [URL]", "Set the iTunesU authentication url.") do |url|
113
+ config["url"] = url
114
+ end
115
+
116
+ opts.on("--debug-suffix [SUFFIX]", "Set the iTunesU debug suffix.") do |str|
117
+ config["debug_suffix"] = str
118
+ end
119
+
120
+ cred_help = "Set the iTunesU credentials. Format: shortName1::LongCred1;shortName2::LongCred2"
121
+ opts.on("--cred [CREDSTR]", cred_help) do |str|
122
+ creds = {}
123
+ str.split(",").each do |pair|
124
+ k,v = pair.split("::")
125
+ k.strip!
126
+ v.strip!
127
+ creds[k.to_sym] = v
128
+ end
129
+ config["creds"] = creds
130
+ end
131
+
132
+ opts.on("--secret [SECRET]", "Set the iTunesU shared secret.") do |str|
133
+ config["shared_secret"] = str
134
+ end
135
+
136
+ opts.separator ""
137
+
138
+ opts.on_tail("-v", "--version", "Display the software version.") do
139
+ puts IAuthU::VERSION
140
+ exit
141
+ end
142
+
143
+ opts.on_tail("-h", "--help", "Show this help message.") do
144
+ puts opts
145
+ exit
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+
152
+ end
153
+ end
154
+
155
+ begin
156
+ IAuthU::Command.call(ARGV)
157
+ rescue => ex
158
+ puts ex.message
159
+ raise ex if GLOBAL_CONFIG[:trace]
160
+ exit
161
+ end
@@ -0,0 +1,63 @@
1
+ module IAuthU
2
+ module Authenticator # :nodoc:
3
+
4
+ =begin rdoc
5
+ IAuthU uses 'Authentication' objects to validate and authenticate users.
6
+ Any object can be used for authentication as long as it follows the following
7
+ conventions:
8
+ - has a #call method that accepts a username and password
9
+ - the #call method returns an identity hash in the form:
10
+ { :username => 'john.doe',
11
+ :display_name => 'John Doe',
12
+ :email => 'john.doe@montana.edu',
13
+ :identifier => '',
14
+ :credentials => [:admin, :user] }
15
+ - the identity hash MUST contain a :username entry
16
+ - if authentication fails the #call method should return nil
17
+
18
+ This convention allows the use of lambda objects as authenticators:
19
+ lambda {|user,pass|
20
+ if user == 'foo'
21
+ {:username => user, :credentials => [:admin]}
22
+ end
23
+ }
24
+
25
+ There are also optional behavior methods that authentication objects
26
+ should, but are not required to implement:
27
+ #required:: specifies that an authentication object is required. In an
28
+ authentication chain, failure of authenticator objects that are required
29
+ causes the entire chain to fail.
30
+ #sufficient:: specifies that an authentication object is sufficient for
31
+ authentication. In an auth chain, authenticator objects that are sufficient
32
+ and successfully authenticate halt the execution of the chain and cause the
33
+ chain to return a successful authentication.
34
+
35
+ If you require custom authentication, subclass IAuthU::Authenticator::Base
36
+ and override #call to perform your custom authentication. You may also use
37
+ any object that implements #call as specified above. If you require multiple
38
+ authentication steps, you may compose authenticators together using
39
+ IAuthU::Authenticator::Chained.
40
+ =end
41
+ class Base
42
+ def call(username, password)
43
+ nil
44
+ end
45
+
46
+ def required
47
+ @required ||= false
48
+ end
49
+
50
+ def required=(bool)
51
+ @required = !!bool
52
+ end
53
+
54
+ def sufficient
55
+ @sufficient ||= false
56
+ end
57
+
58
+ def sufficient=(bool)
59
+ @sufficient = !!bool
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,108 @@
1
+ require File.join(File.dirname(__FILE__), 'base')
2
+ module IAuthU
3
+ module Authenticator # :nodoc:
4
+
5
+ =begin rdoc
6
+ The Chained Authenticator allows multiple authentication objects to
7
+ be combined. The order in which authenticators are added is the order
8
+ in which they are executed. The identity hash that each authenticator
9
+ returns is combined with the one returned by the previous authenticator.
10
+
11
+ local_auth = IAuthU::Authenticator::Chained.build {
12
+ #Use builtin htaccess authenticator
13
+ use IAuthU::Authenticator::FileBased.new('/etc/itunesu/user.auth'), :required => true
14
+
15
+ #Call a custom authenticator
16
+ use CustomLDAPAuth.new
17
+
18
+ #Add default user credential to all identities
19
+ use lambda {|user,pass| {:credentials => [:user]}}
20
+ }
21
+ =end
22
+ class Chained < Base
23
+
24
+ =begin rdoc
25
+ Allows easy construction of chained authenticators. The format for
26
+ specifying the authentication chain is:
27
+ use someAuthenticator [, {:required => true|false, :sufficient => true|false}]
28
+ =end
29
+ def self.build(&block)
30
+ chained = Builder.new
31
+ chained.instance_eval(&block)
32
+ chained.auth
33
+ end
34
+
35
+ # Creates a new Chained authentication object. A list of authenticators
36
+ # can be passed. This will create an authentication chain compoased of
37
+ # the passed authenticators.
38
+ def initialize(*args)
39
+ @chain = args || []
40
+ end
41
+
42
+ # Append an authenticator to the authentication chain.
43
+ def <<(authenticator)
44
+ add authenticator
45
+ end
46
+
47
+ # Append an authenticator to the authentication chain. Optionally specifiy
48
+ # if the authenticator is required or sufficient for the chain. required and
49
+ # sufficient both default to false.
50
+ # chain.add MyAuth.new # MyAuth is not required and not sufficient
51
+ # chain.add MyAuth.new, :required => true, :sufficient => false
52
+ def add(authenticator, opts={})
53
+ authenticator.required = !!opts["required"] if opts["required"] && authenticator.respond_to?('required=')
54
+ authenticator.sufficient = !!opts["sufficient"] if opts ["sufficient"] && authenticator.respond_to?('sufficient=')
55
+ @chain << authenticator
56
+ end
57
+
58
+ # Invoke the authentication chain
59
+ def call(username,password)
60
+ auths = @chain.clone
61
+ identity = {}
62
+ until auths.empty?
63
+ auth = auths.shift
64
+ new_ident = auth.call(username,password)
65
+ if new_ident.nil? && auth.respond_to?(:required) && auth.required
66
+ #Authentication failed for a required authenticator
67
+ return nil
68
+ end
69
+ identity = merge_identities(identity, new_ident)
70
+ if identity && auth.respond_to?(:sufficient) && auth.sufficient
71
+ #This authenticator is sufficient; do not continue down auth chain.
72
+ break
73
+ end
74
+ end
75
+ return nil if identity.empty?
76
+ identity
77
+ end
78
+
79
+ private
80
+ def merge_identities(orig, other)
81
+ ident = {}.merge(orig)
82
+ return ident if other.nil? || other.empty?
83
+ creds = other.delete("credentials") || []
84
+ ident["credentials"] ||= []
85
+ ident["credentials"] << creds
86
+ ident["credentials"].flatten!.uniq!
87
+
88
+ ident.merge(other)
89
+ end
90
+
91
+
92
+ class Builder # :nodoc:
93
+ def initialize
94
+ @auth = Chained.new
95
+ end
96
+
97
+ attr_reader :auth
98
+
99
+ def use(authenticator=nil, opts={}, &block)
100
+ if block && !authenticator
101
+ authenticator = block
102
+ end
103
+ @auth.add authenticator, opts
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,79 @@
1
+ require 'rubygems'
2
+ require 'net/ldap'
3
+
4
+ module IAuthU
5
+ module Authenticator # :nodoc:
6
+ class LDAP
7
+ def self.build(&block)
8
+ b = Builder.new
9
+ b.instance_eval(&block)
10
+ b.ldap_auth
11
+ end
12
+
13
+ def initialize(opts)
14
+ @config = opts
15
+ @login_format = @config.delete(:login_format) || "%s"
16
+ @credentials = @config.delete(:credentials) || []
17
+ use_ssl = @config.delete(:use_ssl) || false
18
+ server_names = @config.delete(:servers) || []
19
+
20
+ @servers = server_names.map do |name|
21
+ conf = @config.clone
22
+ conf[:host] = name
23
+ server = Net::LDAP.new(conf)
24
+ server.encryption(:simple_tls) if use_ssl
25
+ server
26
+ end
27
+ end
28
+
29
+ def call(user,pass)
30
+ @servers.each do |s|
31
+ s.auth(@login_format % user, pass)
32
+ ident = {"username" => user}
33
+ ident["credentials"] = @credentials
34
+ return ident if s.bind
35
+ end
36
+ return nil
37
+ end
38
+
39
+ private
40
+ class Builder
41
+ def initialize
42
+ @config = {}
43
+ end
44
+
45
+ def servers(*svrs)
46
+ @config[:servers] = svrs
47
+ end
48
+
49
+ def port(num)
50
+ @config[:port] = num
51
+ end
52
+
53
+ def base(str)
54
+ @config[:base] = str
55
+ end
56
+
57
+ def login_format(str)
58
+ @config[:login_format] = str
59
+ end
60
+
61
+ def use_ssl(bool=true)
62
+ @config[:use_ssl]
63
+ end
64
+
65
+ def config
66
+ @config
67
+ end
68
+
69
+ def credentials(creds)
70
+ @config[:credentials] = creds.to_a
71
+ end
72
+
73
+ def ldap_auth
74
+ LDAP.new(config)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,90 @@
1
+ require 'rubygems'
2
+ require 'net/http'
3
+ require 'net/https'
4
+ require 'hmac-sha2'
5
+ require 'cgi'
6
+ require 'logger'
7
+
8
+ module IAuthU
9
+
10
+ =begin rdoc
11
+ IAuthU::Request performs the ITunesU authentication step. Usually you will
12
+ not create a Request object manually, but will instead recieve it from
13
+ a Site object. A Request object defers the actual connection to ITunesU
14
+ until the #call method is invoked.
15
+ =end
16
+ class Request
17
+ def initialize(user, creds, site)
18
+ @user = user
19
+ @creds = creds.to_a
20
+ #raise MissingCredentialsError, "Credentials cannot be empty." if @creds.empty?
21
+ @site = site
22
+ @debug = false
23
+ end
24
+
25
+ attr_accessor :debug
26
+
27
+ def logger
28
+ CONFIG[:logger]
29
+ end
30
+
31
+ def call
32
+ logger.info "Sending request for: #{@user.inspect}"
33
+ token, data = get_authorization_token(@user, @creds, @site.shared_secret)
34
+ invoke_action(site_url, data, token)
35
+ end
36
+
37
+ private
38
+
39
+ def site_url
40
+ debug ? "#{@site.url}#{@site.debug_suffix}" : @site.url
41
+ end
42
+
43
+ def identity_string(identity)
44
+ display_name = identity["display_name"]
45
+ email_address = identity["email"]
46
+ username = identity["username"]
47
+ user_identifier = identity["identifier"]
48
+ "\"#{display_name}\" <#{email_address}> (#{username}) [#{user_identifier}]"
49
+ end
50
+
51
+ def get_authorization_token(identity, credentials, key)
52
+ identity = identity_string(identity)
53
+ credentials = credentials * ';'
54
+ token = {}
55
+ expiration = Time.new().to_i
56
+
57
+ #Need to escape using CGI.escape; URI.escape doesn't escape '()<>' properly
58
+ cred = CGI.escape(credentials)
59
+ id = CGI.escape(identity)
60
+ exp = CGI.escape(expiration.to_s)
61
+
62
+ buffer = "credentials=#{cred}&identity=#{id}&time=#{exp}"
63
+
64
+ sig = HMAC::SHA256.new(key)
65
+ sig << buffer
66
+ buffer = buffer + "&signature=#{sig}"
67
+
68
+ token = {
69
+ 'credentials' => credentials,
70
+ 'identity' => identity,
71
+ 'time' => expiration.to_s,
72
+ 'signature' => sig.to_s
73
+ }
74
+ logger.debug "Request Token: #{token.inspect}"
75
+ logger.debug "Request Params: #{buffer.inspect}"
76
+ return token, buffer
77
+ end
78
+
79
+ def invoke_action(site_url, data, token_hdrs)
80
+ uri = URI.parse(site_url)
81
+ http = Net::HTTP.new(uri.host, uri.port)
82
+ http.use_ssl = true
83
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
84
+ #puts token_hdrs.inspect
85
+ logger.debug "Request sent to: #{site_url}"
86
+ response = http.request_post(uri.path, data, token_hdrs)
87
+ return response
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,153 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), 'site')
3
+ require File.join(File.dirname(__FILE__), 'authenticator/chained')
4
+ require 'rack'
5
+ require 'rack/showexceptions'
6
+ require 'rack/request'
7
+ require 'rack/response'
8
+ require 'markaby'
9
+ require 'logger'
10
+
11
+ module IAuthU
12
+
13
+ class Server
14
+ def self.build(&block)
15
+ b = Builder.new
16
+ b.instance_eval(&block)
17
+ b.server
18
+ end
19
+
20
+ def initialize
21
+ @runner = Rack::Handler::WEBrick
22
+ @port = 9292
23
+ @login_page = login_form
24
+ end
25
+ attr_accessor :site, :auth, :runner, :port, :login_page
26
+
27
+ def logger
28
+ CONFIG[:logger]
29
+ end
30
+
31
+ def run
32
+ raise "Site config is required" unless @site
33
+ raise "Auth config is required" unless @auth
34
+ logger.info "Running IAuthU with: #{@runner}"
35
+ @runner.run(Rack::ShowExceptions.new(Rack::Lint.new(self)), :Port => @port)
36
+ end
37
+
38
+ def call(env)
39
+ req = Rack::Request.new(env)
40
+ user, pass = req.POST["username"], req.POST["password"]
41
+ # if user && pass
42
+ # logged_in, result = login(user, pass)
43
+ # else
44
+ # logged_in = false
45
+ # end
46
+ # result = login_page unless logged_in
47
+ # res = Rack::Response.new
48
+ # res.write result
49
+ # res.finish
50
+ unless req.POST["username"] && req.POST["password"]
51
+ Rack::Response.new.finish do |res|
52
+ res.write login_page
53
+ end
54
+ else
55
+ Rack::Response.new.finish do |res|
56
+ logged_in, result = login(req.POST["username"], req.POST["password"])
57
+ #puts result
58
+ res.write result
59
+ end
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ def login(user, pass)
66
+ identity = @auth.call(user,pass)
67
+ if identity
68
+ req = @site.authentication_request(identity)
69
+ resp = req.call
70
+ [:true, resp.body]
71
+ else
72
+ [:false, login_page]
73
+ end
74
+ end
75
+
76
+ def login_form
77
+ page_title = "iTunesU Login"
78
+ m = Markaby::Builder.new
79
+ m.html do
80
+ head { title page_title}
81
+ body do
82
+ h1 page_title
83
+ form(:method => 'post') do
84
+ p {
85
+ label("Username: ", :for => 'username')
86
+ input(:type => 'text', :name => 'username', :id => 'username')
87
+ }
88
+ p {
89
+ label("Password: ", :for => 'password')
90
+ input(:type => 'password', :name => 'password', :id => 'password')
91
+ }
92
+ input(:type => 'submit', :value => 'Login')
93
+ end
94
+ end
95
+ end
96
+ m.to_s
97
+ end
98
+
99
+ class Builder
100
+ RUNNERS = { :cgi => Rack::Handler::CGI,
101
+ #:fastcgi => Rack::Handler::FastCGI,
102
+ :webrick => Rack::Handler::WEBrick,
103
+ :mongrel => Rack::Handler::Mongrel
104
+ }
105
+
106
+ def initialize
107
+ @server = Server.new
108
+ end
109
+
110
+ attr_reader :server
111
+
112
+ def site(&block)
113
+ @server.site = Site.build(&block)
114
+ end
115
+
116
+ def auth(&block)
117
+ @server.auth = Authenticator::Chained.build(&block)
118
+ end
119
+
120
+ def login_page(str_or_file=nil, &block)
121
+ return if str_or_file == :default
122
+ if str_or_file
123
+ if File.exist?(str_or_file)
124
+ File.open(str_or_file) do |f|
125
+ @server.login_page = f.read
126
+ end
127
+ else
128
+ @server.login_page = str_or_file
129
+ end
130
+ return
131
+ else
132
+ @server.login_page = block.call.to_s
133
+ end
134
+ end
135
+
136
+ def logger(dest)
137
+ if dest == :default
138
+ dest = STDERR
139
+ end
140
+ if dest.kind_of?(IO) || dest.kind_of?(String) || dest.kind_of?(Symbol)
141
+ CONFIG[:logger] = Logger.new(dest)
142
+ else
143
+ CONFIG[:logger] = dest
144
+ end
145
+ end
146
+
147
+ def run(type, opts={})
148
+ @server.runner = RUNNERS[type] || RUNNERS[:webrick]
149
+ @server.port ||= opts[:port]
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,82 @@
1
+ require File.join(File.dirname(__FILE__), 'request')
2
+ require 'logger'
3
+ module IAuthU
4
+
5
+ =begin rdoc
6
+ IAuthU::Site represents your remote ITunesU site.
7
+
8
+ site = IAuthU::Site.build {
9
+ url "https://deimos.apple.com/WebObjects/Core.woa/Browse/montana.edu"
10
+ debug_suffix "/aqc392"
11
+ shared_secret "WQBMFYFFH7SBPVFXMCEBG43WC2ANW7LQ"
12
+ cred :admin, "Administrator\@urn:mace:itunesu.com:sites:montana.edu"
13
+ cred :user, "Authenticated\@urn:mace:itunesu.com:sites:montana.edu"
14
+ }
15
+ =end
16
+ class Site
17
+ def self.build(&block)
18
+ builder = Builder.new
19
+ builder.instance_eval(&block)
20
+ builder.site
21
+ end
22
+
23
+ def initialize(opts={})
24
+ @debug = false
25
+ opts = {:url => "", :debug_suffix => "", :shared_secret => "", :credentials => {}}.merge(opts)
26
+ opts.keys.each{|var| instance_variable_set "@#{var.to_s}", opts[var]}
27
+ end
28
+ attr_accessor :url, :debug_suffix, :debug, :shared_secret, :credentials
29
+
30
+ def logger
31
+ CONFIG[:logger]
32
+ end
33
+
34
+ def authentication_request(user)
35
+ raise SettingsError, "Site url must be set before sending request." unless url && !url.empty?
36
+ user = user.clone
37
+ creds = user.delete("credentials") || []
38
+ tmp = []
39
+ creds.each{|c| tmp << credentials[c]}
40
+
41
+ begin
42
+ req = Request.new(user, tmp, self)
43
+ rescue Request::MissingCredentialsError
44
+ raise MissingCredentialsError, "Credentials entry is missing in: #{user.inspect}"
45
+ end
46
+ req.debug = debug
47
+ req
48
+ end
49
+
50
+ class SettingsError < RuntimeError # :nodoc:
51
+ end
52
+
53
+ class Builder # :nodoc:
54
+ def initialize
55
+ @site = Site.new
56
+ end
57
+
58
+ attr_reader :site
59
+
60
+ def url(url)
61
+ @site.url = url
62
+ end
63
+
64
+ def debug_suffix(suffix)
65
+ @site.debug_suffix = suffix
66
+ end
67
+
68
+ def debug(bool=true)
69
+ @site.debug = bool
70
+ end
71
+
72
+ def shared_secret(str)
73
+ @site.shared_secret = str
74
+ end
75
+
76
+ def cred(name, credential)
77
+ name = name.to_sym
78
+ @site.credentials[name] = credential.to_s
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,9 @@
1
+ module IAuthU #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
data/lib/iauthu.rb ADDED
@@ -0,0 +1,9 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'iauthu/server'
5
+ require 'logger'
6
+ module IAuthU
7
+ VERSION = '0.0.1'
8
+ CONFIG = {:logger => Logger.new(STDERR)}
9
+ end
@@ -0,0 +1,82 @@
1
+ # Specify how IAuthU should run.
2
+ # Available options are: :cgi, :webrick, :mongrel
3
+ # Note: if a cgi environment is detected, IAuthU will run in
4
+ # cgi mode regardless of this setting.
5
+
6
+ run :webrick, :port => 9292
7
+
8
+ # Specify the log destination:
9
+ # * :default == STDERR
10
+ # * "/path/to/logfile"
11
+ # * a Logger object
12
+ # note: using STDOUT will interfere with cgi output
13
+
14
+ logger :default # :default | "/var/log/iauthu.log" | Logger.new("/var/logs/iauthu.log")
15
+
16
+
17
+ # Specify the Login Page
18
+ # Options include:
19
+ # * :default - displays the builtin login form
20
+ # * "/path/to/file.html" - displays specified file
21
+ # * "<html><title>...." - use a raw html string
22
+ # * {...} - run a ruby block to generate the page.
23
+ # For example, consider using markaby
24
+
25
+ login_page :default
26
+
27
+ site {
28
+ # Specify your iTunesU url
29
+ url "<%= url || "https://deimos.apple.com/WebObjects/Core.woa/Browse/foo.edu" %>"
30
+
31
+ # Specify your debug suffix if you wish to use iTunesU authentication
32
+ # in debug mode.
33
+ <%= "#" unless debug_suffix %> debug_suffix "<%= debug_suffix || "/debug_suffix" %>"
34
+
35
+ # Enable iTunesU authentication debugging. Must specify the debug_suffix.
36
+ debug <%= debug ? "true" : "false" %>
37
+
38
+ # Shared authentication secret
39
+ shared_secret "<%= shared_secret || "SHARED-SECRET" %>"
40
+
41
+ # Define your iTunesU login credentials. The alias you specify
42
+ # can be used to refer to that credential later in the authentication
43
+ # section.
44
+ #
45
+ # Example:
46
+ # cred :alias, "FullCred@urn:mace:itunesu.com:sites:urschool.edu"
47
+ <% unless creds.nil?
48
+ creds.keys.each do |key| %>
49
+ cred <%= key.inspect %>, <%= creds[key].inspect %>
50
+ <% end
51
+ end %>
52
+ }
53
+
54
+ auth {
55
+
56
+ # Authenticate all users
57
+ use {|user, pass|
58
+ {"display_name" => user,
59
+ "username" => user,
60
+ "credentials" => []
61
+ }
62
+ }
63
+
64
+ # # Uncomment to Authenticate Users from LDAP
65
+ # require 'iauthu/authenticator/ldap'
66
+ # use IAuthU::Authenticator::LDAP.build {
67
+ # servers "ldap.urschool.edu"
68
+ # login_format "uid=%s,ou=People,o=ldap.urschool.edu,o=cp"
69
+ # credentials [:user]
70
+ # }
71
+
72
+
73
+ # # Uncomment to create a 'test' admin user
74
+ # use {|user, pass|
75
+ # if user == 'test' && pass == 'test'
76
+ # {"display_name" => "Site Administrator",
77
+ # "email" => "acg-support@montana.edu",
78
+ # "credentials" => [:admin]}
79
+ # end
80
+ # }
81
+ }
82
+
File without changes
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: iauthu
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Heimbuch
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-06-11 00:00:00 -06:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rack
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.3.0
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: mongrel
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 1.1.5
32
+ version:
33
+ - !ruby/object:Gem::Dependency
34
+ name: markaby
35
+ version_requirement:
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: "0.5"
41
+ version:
42
+ - !ruby/object:Gem::Dependency
43
+ name: ruby-hmac
44
+ version_requirement:
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 0.3.1
50
+ version:
51
+ - !ruby/object:Gem::Dependency
52
+ name: ruby-net-ldap
53
+ version_requirement:
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: 0.0.4
59
+ version:
60
+ - !ruby/object:Gem::Dependency
61
+ name: hoe
62
+ version_requirement:
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: 1.5.3
68
+ version:
69
+ description: IAuthU provides a basic iTunesU authentication server, along with libraries for building iTunesU authentication servers into your own application.
70
+ email:
71
+ - rheimbuch@gmail.com
72
+ executables:
73
+ - iauthu
74
+ extensions: []
75
+
76
+ extra_rdoc_files:
77
+ - History.txt
78
+ - Manifest.txt
79
+ - README.txt
80
+ files:
81
+ - History.txt
82
+ - Manifest.txt
83
+ - README.txt
84
+ - Rakefile
85
+ - bin/iauthu
86
+ - lib/iauthu.rb
87
+ - lib/iauthu/authenticator/base.rb
88
+ - lib/iauthu/authenticator/chained.rb
89
+ - lib/iauthu/authenticator/ldap.rb
90
+ - lib/iauthu/request.rb
91
+ - lib/iauthu/server.rb
92
+ - lib/iauthu/site.rb
93
+ - lib/iauthu/version.rb
94
+ - template/iauthu.conf.tmpl
95
+ - test/test_iauthu.rb
96
+ has_rdoc: true
97
+ homepage: http://github.com/rheimbuch/iauthu
98
+ post_install_message:
99
+ rdoc_options:
100
+ - --main
101
+ - README.txt
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: "0"
109
+ version:
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: "0"
115
+ version:
116
+ requirements: []
117
+
118
+ rubyforge_project: iauthu
119
+ rubygems_version: 1.0.1
120
+ signing_key:
121
+ specification_version: 2
122
+ summary: IAuthU provides a basic iTunesU authentication server, along with libraries for building iTunesU authentication servers into your own application.
123
+ test_files:
124
+ - test/test_iauthu.rb