iauthu 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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