Convert OpenID authentication to use OmniAuth
[rails.git] / vendor / gems / rots-0.2.1 / lib / rots / server_app.rb
1 require 'openid'
2 require 'openid/extension'
3 require 'openid/extensions/sreg'
4 require 'openid/store/filesystem'
5 require 'openid/util'
6 require 'rack/request'
7 require 'rack/utils'
8 require 'fileutils'
9
10
11 module Rots
12   
13   class ServerApp
14     
15     attr_accessor :request,:openid_request,
16                   :response, :openid_response,
17                   :server
18     
19     def initialize(config, server_options)
20       @server_options = server_options
21       @sreg_fields = config['sreg']
22     end
23     
24     def call(env)
25       on_openid_request(env) do
26         if !is_checkid_request?
27           @openid_response = @server.handle_request(@openid_request)
28           reply_consumer
29         elsif is_checkid_immediate?
30           process_immediate_checkid_request
31         else
32           process_checkid_request
33         end
34       end
35     end
36     
37     protected
38     
39     def on_openid_request(env)
40       create_wrappers(env)
41       if @openid_request.nil?
42         [200, {'Content-Type' => 'text/html'}, 
43           ["<html><body><h1>ROTS => This is an OpenID endpoint</h1></body></html>"] ]
44       else
45         yield
46       end
47     end
48     
49     def create_wrappers(env)
50       @request = Rack::Request.new(env)
51       @server  = OpenID::Server::Server.new(storage, op_endpoint)
52       @openid_request = @server.decode_request(@request.params)
53       @openid_sreg_request = OpenID::SReg::Request.from_openid_request(@openid_request) unless @openid_request.nil?
54     end
55     
56     def is_checkid_request?
57       @openid_request.is_a?(OpenID::Server::CheckIDRequest)
58     end
59     
60     def is_checkid_immediate?
61       @openid_request && @openid_request.immediate
62     end
63     
64     def process_immediate_checkid_request
65       # TODO: We should enable the user to configure
66       # if she wants immediate request support or not
67       url = OpenID::Util.append_args(@openid_request.return_to, 
68         @request.params.merge('openid.mode' => 'setup_needed'))
69       redirect(url)
70     end
71     
72     def process_checkid_request
73       if checkid_request_is_valid?
74         return_successful_openid_response
75       else
76         return_cancel_openid_response
77       end
78     end
79     
80     def checkid_request_is_valid?
81       @request.params['openid.success'] == 'true'
82     end
83     
84     def return_successful_openid_response
85       @openid_response = @openid_request.answer(true)
86       process_sreg_extension
87       # TODO: Add support for SREG extension
88       @server.signatory.sign(@openid_response) if @openid_response.needs_signing
89       reply_consumer
90     end
91     
92     def process_sreg_extension
93       return if @openid_sreg_request.nil?
94       response = OpenID::SReg::Response.extract_response(@openid_sreg_request, @sreg_fields)
95       @openid_response.add_extension(response)
96     end
97     
98     def return_cancel_openid_response
99       redirect(@openid_request.cancel_url)
100     end
101     
102     def reply_consumer
103       web_response = @server.encode_response(@openid_response)
104       case web_response.code
105       when OpenID::Server::HTTP_OK
106         success(web_response.body)
107       when OpenID::Server::HTTP_REDIRECT
108         redirect(web_response.headers['location'])
109       else
110         bad_request
111       end   
112     end
113
114     def redirect(uri)
115       [ 303, {'Content-Length'=>'0', 'Content-Type'=>'text/plain',
116         'Location' => uri},
117         [] ]
118     end
119
120     def bad_request()
121       [ 400, {'Content-Type'=>'text/plain', 'Content-Length'=>'0'},
122         [] ]
123     end
124     
125     def storage
126       # create the folder if it doesn't exist
127       FileUtils.mkdir_p(@server_options[:storage]) unless File.exist?(@server_options[:storage])
128       OpenID::Store::Filesystem.new(@server_options[:storage])
129     end
130     
131     def success(text="")
132       Rack::Response.new(text).finish
133     end
134     
135     def op_endpoint
136       if @request.url =~ /(.*\?openid.success=true)/
137         $1
138       elsif @request.url =~ /([^?]*)/
139         $1
140       else
141         nil
142       end
143     end
144
145   end
146
147 end