In Files

Parent

Class Index [+]

Quicksearch

Object

Public Instance Methods

add_pape(oidreq, oidresp) click to toggle source

(Not documented)

     # File bin/local-openid, line 167
167: def add_pape(oidreq, oidresp)
168:   papereq = OpenID::PAPE::Request.from_openid_request(oidreq) or return
169:   paperesp = OpenID::PAPE::Response.new(papereq.preferred_auth_policies,
170:                                         papereq.max_auth_age)
171:   # since this implementation requires shell/filesystem access to the
172:   # OpenID server to authenticate, we can say we're at the highest
173:   # auth level possible...
174:   paperesp.add_policy_uri(OpenID::PAPE::AUTH_MULTI_FACTOR_PHYSICAL)
175:   paperesp.auth_time = Time.now.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
176:   paperesp.nist_auth_level = 4
177:   oidresp.add_extension(paperesp)
178: end
add_sreg(oidreq, oidresp) click to toggle source

support the simple registration extension if possible, allow per-site overrides of various data points

     # File bin/local-openid, line 154
154: def add_sreg(oidreq, oidresp)
155:   sregreq = OpenID::SReg::Request.from_openid_request(oidreq) or return
156:   per_site = config[oidreq.trust_root] || {}
157: 
158:   sreg_data = {}
159:   sregreq.all_requested_fields.each do |field|
160:     sreg_data[field] = per_site[field] || config[field]
161:   end
162: 
163:   sregresp = OpenID::SReg::Response.extract_response(sregreq, sreg_data)
164:   oidresp.add_extension(sregresp)
165: end
big_lock(&block) click to toggle source

if a single-user OpenID provider like us is being hit by multiple clients at once, then something is seriously wrong. Can’t use Mutexes here since somebody could be running this as a CGI script

     # File bin/local-openid, line 289
289: def big_lock(&block)
290:   lock = "#$local_openid/lock"
291:   File.open(lock, File::WRONLY|File::CREAT|File::EXCL, 0600) do |fp|
292:     begin
293:       yield
294:     ensure
295:       File.unlink(lock)
296:     end
297:   end
298:   rescue Errno::EEXIST
299:     err("Lock: #{lock} exists! Possible hijacking attempt") rescue nil
300: end
config() click to toggle source

(Not documented)

     # File bin/local-openid, line 238
238: def config
239:   @config ||= begin
240:     YAML.load(File.read("#$local_openid/config.yml"))
241:   rescue Errno::ENOENT
242:     {}
243:   end
244: end
err(msg) click to toggle source

(Not documented)

     # File bin/local-openid, line 180
180: def err(msg)
181:   env['rack.errors'].write("#{msg}\n")
182:   false
183: end
finalize_response(oidresp) click to toggle source

(Not documented)

     # File bin/local-openid, line 185
185: def finalize_response(oidresp)
186:   server.signatory.sign(oidresp) if oidresp.needs_signing
187:   web_response = server.encode_response(oidresp)
188: 
189:   case web_response.code
190:   when HTTP_OK
191:     web_response.body
192:   when HTTP_REDIRECT
193:     location = web_response.headers['location']
194:     err("redirecting to: #{location} ...")
195:     redirect(location)
196:   else
197:     halt(500, web_response.body)
198:   end
199: end
get_or_post() click to toggle source

this is the heart of our provider logic, adapted from the Ruby OpenID gem Rails example

     # File bin/local-openid, line 102
102: def get_or_post
103:   oidreq = begin
104:     server.decode_request(params)
105:   rescue ProtocolError => err
106:     halt(500, err.to_s)
107:   end
108: 
109:   oidreq or return render_xrds
110: 
111:   oidresp = case oidreq
112:   when CheckIDRequest
113:     if oidreq.id_select && oidreq.immediate
114:       oidreq.answer(false)
115:     elsif is_authorized?(oidreq)
116:       resp = oidreq.answer(true, nil, server_root)
117:       add_sreg(oidreq, resp)
118:       add_pape(oidreq, resp)
119:       resp
120:     elsif oidreq.immediate
121:       oidreq.answer(false, server_root)
122:     else
123:       session[:id] ||= "#{Time.now.to_i}.#$$.#{rand}"
124:       session[:ip] = request.ip
125:       merge_config(oidreq)
126:       write_config
127: 
128:       # here we allow our user to open $EDITOR and edit the appropriate
129:       # 'expires' field in config.yml corresponding to oidreq.trust_root
130:       return PROMPT.gsub(/%s/, oidreq.trust_root)
131:     end
132:   else
133:     server.handle_request(oidreq)
134:   end
135: 
136:   finalize_response(oidresp)
137: end
is_authorized?(oidreq) click to toggle source

the heart of our custom authentication logic

     # File bin/local-openid, line 202
202: def is_authorized?(oidreq)
203:   (config['allowed_ips'] ||= []).include?(request.ip) or
204:     return err("Not allowed: #{request.ip}\n" \
205:                "You need to put this IP in the 'allowed_ips' array "\
206:                "in:\n #$local_openid/config.yml")
207: 
208:   request.ip == session[:ip] or
209:     return err("session IP mismatch: " \
210:                "#{request.ip.inspect} != #{session[:ip].inspect}")
211: 
212:   trust_root = oidreq.trust_root
213:   per_site = config[trust_root] or
214:     return err("trust_root unknown: #{trust_root}")
215: 
216:   session_id = session[:id] or return err("no session ID")
217: 
218:   assoc_handle = per_site['assoc_handle'] # this may be nil
219:   expires = per_site['expires'] or
220:     return err("no expires (trust_root=#{trust_root})")
221: 
222:   assoc_handle == oidreq.assoc_handle or
223:     return err("assoc_handle mismatch: " \
224:                "#{assoc_handle.inspect} != #{oidreq.assoc_handle.inspect}" \
225:                " (trust_root=#{trust_root})")
226: 
227:   per_site['session_id'] == session_id or
228:     return err("session ID mismatch: " \
229:                "#{per_site['session_id'].inspect} != #{session_id.inspect}" \
230:                " (trust_root=#{trust_root})")
231: 
232:   expires > Time.now or
233:     return err("Expired: #{expires.inspect} (trust_root=#{trust_root})")
234: 
235:   true
236: end
merge_config(oidreq) click to toggle source

(Not documented)

     # File bin/local-openid, line 246
246: def merge_config(oidreq)
247:   per_site = config[oidreq.trust_root] ||= {}
248:   per_site.merge!({
249:       'assoc_handle' => oidreq.assoc_handle,
250:       'expires' => Time.at(0).utc,
251:       'updated' => Time.now.utc,
252:       'expires1m' => Time.now.utc + 60, # easy edit to "expires" in $EDITOR
253:       'session_id' => session[:id],
254:     })
255: end
render_xrds(force = false) click to toggle source

this output is designed to be parsed by OpenID consumers

     # File bin/local-openid, line 269
269: def render_xrds(force = false)
270:   if force || request.accept.include?('application/xrds+xml')
271: 
272:     # this seems to work...
273:     types = request.accept.include?('application/xrds+xml') ?
274:        [ OpenID::OPENID_2_0_TYPE, OpenID::OPENID_1_0_TYPE, OpenID::SREG_URI ] :
275:        [ OpenID::OPENID_IDP_2_0_TYPE ]
276: 
277:     headers['Content-Type'] = 'application/xrds+xml'
278:     types = types.map { |uri| "<Type>#{uri}</Type>" }.join("\n")
279:     XRDS_XML.gsub(/%s/, server_root).gsub!(/%types/, types)
280:   else # render a browser-friendly page with an XRDS pointer
281:     headers['X-XRDS-Location'] = "#{server_root}xrds"
282:     XRDS_HTML.gsub(/%s/, server_root)
283:   end
284: end
server() click to toggle source

(Not documented)

     # File bin/local-openid, line 146
146: def server
147:   @server ||= Server.new(
148:       OpenID::Store::Filesystem.new("#$local_openid/store"),
149:       server_root)
150: end
server_root() click to toggle source

we’re the provider for exactly one identity. However, we do rely on being proxied and being hit with an appropriate HTTP Host: header. Don’t expect OpenID consumers to handle port != 80.

     # File bin/local-openid, line 142
142: def server_root
143:   "http://#{request.host}/"
144: end
write_config() click to toggle source

(Not documented)

     # File bin/local-openid, line 257
257: def write_config
258:   path = "#$local_openid/config.yml"
259:   tmp = Tempfile.new('config.yml', File.dirname(path))
260:   tmp.syswrite(CONFIG_HEADER.gsub(/^/m, "# "))
261:   tmp.syswrite(config.to_yaml)
262:   tmp.syswrite(CONFIG_TRAILER.gsub(/^/m, "# "))
263:   tmp.fsync
264:   File.rename(tmp.path, path)
265:   tmp.close!
266: end

Disabled; run with --debug to generate this.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.