# # Author:: Adam Jacob () # Copyright:: Copyright (c) 2008 HJK Solutions, LLC # License:: Apache License, Version 2.0 # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'pathname' # load the openid library, first trying rubygems #begin # require "rubygems" # require_gem "ruby-openid", ">= 1.0" #rescue LoadError require "openid" require "openid/consumer/discovery" require 'openid/store/filesystem' require 'json' #end class OpenidServer < Application provides :html, :json include Merb::OpenidServerHelper include OpenID::Server layout nil before :fix_up_node_id def index oidreq = server.decode_request(params.reject{|k,v| k == "controller" || k == "action"}) # no openid.mode was given unless oidreq return "This is the Chef OpenID server endpoint." end oidresp = nil if oidreq.kind_of?(CheckIDRequest) identity = oidreq.identity if oidresp nil elsif self.is_authorized(identity, oidreq.trust_root) oidresp = oidreq.answer(true, nil, identity) elsif oidreq.immediate server_url = url :openid_server oidresp = oidreq.answer(false, server_url) else if content_type != 'application/json' session[:last_oidreq] = oidreq response = { :action => url(:openid_server_decision) } return response.to_json else return show_decision_page(oidreq) end end else oidresp = server.handle_request(oidreq) end self.render_response(oidresp) end def show_decision_page(oidreq, message="Do you trust this site with your identity?") session[:last_oidreq] = oidreq @oidreq = oidreq if message session[:notice] = message end render :template => 'openid_server/decide' end def node_page unless Chef::OpenIDRegistration.has_key?(params[:id]) raise NotFound, "Cannot find registration for #{params[:id]}" end # Yadis content-negotiation: we want to return the xrds if asked for. accept = request.env['HTTP_ACCEPT'] # This is not technically correct, and should eventually be updated # to do real Accept header parsing and logic. Though I expect it will work # 99% of the time. if accept and accept.include?('application/xrds+xml') return node_xrds end # content negotiation failed, so just render the user page xrds_url = absolute_url(:openid_node_xrds, :id => params[:id]) identity_page = <

OpenID identity page for registration #{params[:id]}

EOS # Also add the Yadis location header, so that they don't have # to parse the html unless absolutely necessary. @headers['X-XRDS-Location'] = xrds_url render identity_page end def node_xrds types = [ OpenID::OPENID_2_0_TYPE, OpenID::OPENID_1_0_TYPE ] render_xrds(types) end def idp_xrds types = [ OpenID::OPENID_IDP_2_0_TYPE, ] render_xrds(types) end def decision oidreq = session[:last_oidreq] session[:last_oidreq] = nil if params.has_key?(:cancel) Merb.logger.info("Cancelling OpenID Authentication") return(redirect(oidreq.cancel_url)) else identity = oidreq.identity identity =~ /node\/(.+)$/ openid_node = Chef::OpenIDRegistration.load($1) unless openid_node.validated raise Unauthorized, "This nodes registration has not been validated" end if openid_node.password == encrypt_password(openid_node.salt, params[:password]) if session[:approvals] session[:approvals] << oidreq.trust_root else session[:approvals] = [oidreq.trust_root] end oidresp = oidreq.answer(true, nil, identity) return self.render_response(oidresp) else raise Unauthorized, "Invalid credentials" end end end protected def encrypt_password(salt, password) Digest::SHA1.hexdigest("--#{salt}--#{password}--") end def server if @server.nil? server_url = absolute_url(:openid_server) dir = Chef::Config[:openid_store_path] store = OpenID::Store::Filesystem.new(dir) @server = Server.new(store, server_url) end return @server end def approved(trust_root) return false if session[:approvals].nil? return session[:approvals].member?(trust_root) end def is_authorized(identity_url, trust_root) return (session[:username] and (identity_url == url_for_user) and self.approved(trust_root)) end def render_xrds(types) type_str = "" types.each { |uri| type_str += "#{uri}\n " } yadis = < #{type_str} #{absolute_url(:openid_server)} EOS @headers['content-type'] = 'application/xrds+xml' render yadis end def render_response(oidresp) if oidresp.needs_signing signed_response = server.signatory.sign(oidresp) end web_response = server.encode_response(oidresp) case web_response.code when HTTP_OK @status = 200 render web_response.body when HTTP_REDIRECT redirect web_response.headers['location'] else @status = 400 render web_response.body end end end