Created
November 21, 2013 12:33
-
-
Save larsar/7580819 to your computer and use it in GitHub Desktop.
FEIDE authentication controllers for Rails, using "omniauth". Code written mostly by Christian Martin. https://github.com/cmartin81
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class AuthenticationsController < ApplicationController | |
| def index | |
| if current_user | |
| @authentications = current_user.authentications | |
| else | |
| redirect_to root_path | |
| end | |
| end | |
| def create | |
| omniauth = request.env["omniauth.auth"] | |
| provider = omniauth['provider'] | |
| if provider == "saml" | |
| uid = omniauth[:extra][:raw_info][:eduPersonPrincipalName] | |
| ext_session_id = omniauth[:uid] | |
| provider = "feide" | |
| else | |
| uid = omniauth['uid'] | |
| end | |
| authentication = Authentication.find_by_provider_and_uid(provider, uid) | |
| if authentication | |
| unless authentication.user.banned? | |
| flash[:notice] = t('authentications.controller.provider_signed_in', {:provider => Authentication.provider_title(provider)}) | |
| authentication.user.update_attribute(:ext_session_id, ext_session_id) if ext_session_id | |
| sign_in_and_redirect(:user, authentication.user) | |
| else | |
| flash[:error] = "This account has been suspended." | |
| redirect_to root_path | |
| end | |
| elsif current_user | |
| current_user.authentications.create!(:provider => provider, :uid => uid) | |
| flash[:notice] = t('authentications.controller.linked_provider', {:provider => Authentication.provider_title(provider)}) | |
| redirect_to authentications_url | |
| else | |
| flash[:error] = t('authentications.controller.provider_sign_in_failed', {:provider => Authentication.provider_title(provider)}) | |
| redirect_to new_user_session_path | |
| end | |
| end | |
| def destroy | |
| @authentication = current_user.authentications.find(params[:id]) | |
| provider = @authentication.provider | |
| @authentication.destroy | |
| flash[:notice] = t('authentications.controller.unlinked_provider') | |
| redirect_to authentications_url | |
| end | |
| def failure | |
| omniauth = request.env["omniauth.auth"] | |
| if omniauth | |
| provider = omniauth['provider'] | |
| if provider == "saml" | |
| provider = "feide" | |
| end | |
| flash[:error] = t('authentications.controller.provider_failure')+": "+params[:message] | |
| else | |
| flash[:error] = t('authentications.controller.general_failure', :message => params[:message]) | |
| end | |
| redirect_to new_user_session_path | |
| end | |
| def handle_unverified_request | |
| # Workaround for session reset problem with SAML | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require 'onelogin/ruby-saml/response' | |
| require 'onelogin/ruby-saml/logoutrequest' | |
| require "cgi" | |
| class FeideController < ApplicationController | |
| STATUS_SUCCESS= 'Success' | |
| STATUS_REQUESTER= 'Requester' | |
| STATUS_RESPONDER= 'Responder' | |
| def request_feide_logout | |
| ext_session = params[:ext_session_id] | |
| request = vaild_request({:nameID => ext_session, :sessionIndex => ext_session ,:saml_settings => saml_settings}) | |
| user = User.find_by_ext_session_id ext_session | |
| user.update_attribute(:ext_session_id, nil) unless user.nil? | |
| redirect_to "#{saml_settings.assertion_consumer_logout_service_url}?SAMLRequest=#{prepare_saml_parameter(request)}" | |
| end | |
| def consume | |
| if params[:SAMLRequest] #incoming request from FEIDE. | |
| #MUST check the parameter against the signature | |
| result=handle_request_callback | |
| redirect_to result | |
| return | |
| elsif params[:SAMLResponse] #incoming response from FEIDE (After we initiated logout) | |
| #MUST check the parameter against the signature | |
| result= handle_response_callback | |
| flash[:notice] = t('user.signed_out') | |
| redirect_to root_path | |
| end | |
| end | |
| private | |
| def handle_request_callback | |
| decode_SAMLRequest(params[:SAMLRequest]) | |
| user = User.find_by_ext_session_id @saml_session | |
| if user.nil? | |
| #Handle if unvalid response | |
| logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response({:id => @saml_request_id, :saml_settings => saml_settings}, STATUS_RESPONDER), saml_settings) | |
| else | |
| sign_out(user) | |
| user.update_attribute(:ext_session_id, nil) | |
| logoutresponse = Onelogin::Saml::Logoutresponse.new(valid_response({:id => @saml_request_id, :saml_settings => saml_settings}), saml_settings) | |
| end | |
| return "#{saml_settings.assertion_consumer_logout_service_url}?SAMLResponse=#{prepare_saml_parameter(logoutresponse.response)}" | |
| end | |
| def handle_response_callback | |
| decode_SAMLResponse(params[:SAMLResponse]) | |
| true | |
| end | |
| def saml_settings | |
| settings = Onelogin::Saml::Settings.new | |
| settings.assertion_consumer_logout_service_url = Settings.feide.slo_target | |
| settings.issuer = Settings.feide.issuer | |
| settings.idp_sso_target_url = Settings.feide.sso_target | |
| settings.idp_cert = Base64.decode64(Settings.feide.idp_cert) | |
| settings.idp_slo_target_url = Settings.feide.slo_target | |
| settings | |
| end | |
| def decode_SAMLRequest(saml_request) | |
| @saml_request = decode_string saml_request | |
| @saml_request_id = @saml_request[/ID=['"](.+?)['"]/, 1] | |
| @saml_issueInstand = @saml_request[/IssueInstant=['"](.+?)['"]/, 1] | |
| @saml_session = @saml_request[/<saml:NameID.*>(.+?)<\/saml:NameID>/, 1] | |
| end | |
| def decode_SAMLResponse(saml_response) | |
| @saml_request = decode_string saml_response | |
| @saml_session = @saml_request[/ID=['"](.+?)['"]/, 1] | |
| @saml_status = @saml_request[/StatusCode Value="=['"](.+?)['"]/, 1] | |
| end | |
| def decode_string(saml_input) | |
| zstream = Zlib::Inflate.new(-Zlib::MAX_WBITS) | |
| saml_xml = zstream.inflate(Base64.decode64(saml_input)) | |
| zstream.finish | |
| zstream.close | |
| return saml_xml | |
| end | |
| def prepare_saml_parameter(response) | |
| deflated = Zlib::Deflate.deflate(response, 9)[2..-5] | |
| encoded_result = Base64.encode64 deflated | |
| return CGI.escape encoded_result | |
| end | |
| def valid_response(opts = {}, status = STATUS_SUCCESS) | |
| "<samlp:LogoutResponse xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"#{random_id}\" Version=\"2.0\" IssueInstant=\"#{Time.now.strftime('%Y-%m-%dT%H:%M:%SZ')}\" Destination=\"#{saml_settings.assertion_consumer_logout_service_url}\" InResponseTo=\"#{opts[:id]}\"><saml:Issuer>#{saml_settings.issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:#{status}\"/></samlp:Status></samlp:LogoutResponse>" | |
| end | |
| #nameID | |
| #sessionIndex | |
| def vaild_request(opts = {}) | |
| "<samlp:LogoutRequest xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" ID=\"#{random_id}\" Version=\"2.0\" Destination=\"#{saml_settings.assertion_consumer_logout_service_url}\" IssueInstant=\"#{Time.now.strftime('%Y-%m-%dT%H:%M:%SZ')}\"> <saml:Issuer > #{saml_settings.issuer} </saml:Issuer> <saml:NameID Format=\"urn:oasis:names:tc:SAML:2.0:nameid-format:transient\" SPNameQualifier=\"#{saml_settings.issuer}\">#{opts[:nameID]} </saml:NameID> <samlp:SessionIndex>#{opts[:sessionIndex]} </samlp:SessionIndex></samlp:LogoutRequest>" | |
| end | |
| def random_id | |
| "_#{UUID.new.generate}" | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # initializers/omniauth.rb | |
| Rails.application.config.middleware.use OmniAuth::Builder do | |
| provider :saml, | |
| :assertion_consumer_service_url => Settings.feide.consumer_url, | |
| :issuer => Settings.feide.issuer, | |
| :idp_sso_target_url => Settings.feide.sso_target, | |
| :idp_cert => Base64.decode64(Settings.feide.idp_cert || "unset"), | |
| :name_identifier_format => "urn:oasis:names:tc:SAML:2.0:nameid-format:transient" | |
| provider :twitter, Settings.twitter.key, Settings.twitter.secret | |
| provider :facebook, Settings.facebook.key, Settings.facebook.secret, :scope => 'email' | |
| provider :google_oauth2, Settings.google.key, Settings.google.secret, {} | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| match '/feide/slo', :controller => 'feide', :action => 'consume' | |
| match '/feide/request_feide_logout', :controller => 'feide', :action => 'request_feide_logout' | |
| match '/auth/:provider/callback' => 'authentications#create' | |
| match '/auth/failure', :controller => 'authentications', :action => 'failure' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment