lib/saml.rb
require 'active_support/all'
require 'active_support/xml_mini'
require 'active_model'
require 'saml/base'
require 'saml/xml_helpers'
require 'saml/encoding'
require 'saml/util'
require 'saml/notification'
require 'saml/attribute_fetcher'
require 'xmlenc'
require 'xmldsig'
require 'net/https'
require 'uri'
module Saml
MD_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:metadata'
MD_RPI_NAMESPACE = 'urn:oasis:names:tc:SAML:metadata:rpi'
MD_ATTR_NAMESPACE = 'urn:oasis:names:tc:SAML:metadata:attribute'
ATTR_EXT_NAMESPACE = 'urn:oasis:names:tc:SAML:attributes:ext'
SAML_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:assertion'
SAMLP_NAMESPACE = 'urn:oasis:names:tc:SAML:2.0:protocol'
XS_NAMESPACE = 'http://www.w3.org/2001/XMLSchema'
XSI_NAMESPACE = 'http://www.w3.org/2001/XMLSchema-instance'
XML_DSIG_NAMESPACE = 'http://www.w3.org/2000/09/xmldsig#'
SAML_VERSION = '2.0'
module Errors
class SamlError < StandardError
end
class SignatureInvalid < SamlError
end
class SignatureMissing < SamlError
end
class InvalidProvider < SamlError
end
class UnparseableMessage < SamlError
end
class InvalidParams < SamlError
end
class MetadataDownloadFailed < SamlError
end
class InvalidStore < SamlError
def initialize(store = '')
@store = store
end
def message
if @store.nil? || @store == ''
'Store cannot be blank'
else
"Store #{@store} not registered"
end
end
end
end
module TopLevelCodes
SUCCESS = 'urn:oasis:names:tc:SAML:2.0:status:Success'
REQUESTER = 'urn:oasis:names:tc:SAML:2.0:status:Requester'
RESPONDER = 'urn:oasis:names:tc:SAML:2.0:status:Responder'
VERSION_MISMATCH = 'urn:oasis:names:tc:SAML:2.0:status:VersionMismatch'
ALL = [SUCCESS, REQUESTER, RESPONDER, VERSION_MISMATCH]
end
module SubStatusCodes
AUTHN_FAILED = 'urn:oasis:names:tc:SAML:2.0:status:AuthnFailed'
NO_AUTHN_CONTEXT = 'urn:oasis:names:tc:SAML:2.0:status:NoAuthnContext'
PARTIAL_LOGOUT = 'urn:oasis:names:tc:SAML:2.0:status:PartialLogout'
REQUEST_DENIED = 'urn:oasis:names:tc:SAML:2.0:status:RequestDenied'
REQUEST_UNSUPPORTED = 'urn:oasis:names:tc:SAML:2.0:status:RequestUnsupported'
UNKNOWN_PRINCIPAL = 'urn:oasis:names:tc:SAML:2.0:status:UnknownPrincipal'
ALL = [AUTHN_FAILED, NO_AUTHN_CONTEXT, PARTIAL_LOGOUT, REQUEST_DENIED, REQUEST_UNSUPPORTED, UNKNOWN_PRINCIPAL]
end
module Bindings
require 'saml/bindings/http_artifact'
require 'saml/bindings/http_redirect'
require 'saml/bindings/http_post'
require 'saml/bindings/soap'
end
module ClassRefs
UNSPECIFIED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified'
PASSWORD_PROTECTED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport'
MOBILE_TWO_FACTOR_UNREGISTERED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorUnregistered'
MOBILE_TWO_FACTOR_CONTRACT = 'urn:oasis:names:tc:SAML:2.0:ac:classes:MobileTwoFactorContract'
MOBILE_SMARTCARD_PKI = 'urn:oasis:names:tc:SAML:2.0:ac:classes:SmartcardPKI'
INTERNET_PROTOCOL = 'urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocol'
INTERNET_PROTOCOL_PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:InternetProtocolPassword'
KERBEROS = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Kerberos'
MOBILE_ONE_FACTOR_UNREGISTERED = 'urn:oasis:names:tc:SAML:2.0:ac:classes:MobileOneFactorUnregistered'
MOBILE_ONE_FACTOR_CONTRACT = 'urn:oasis:names:tc:SAML:2.0:ac:classes:MobileOneFactorContract'
PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Password'
PREVIOUS_SESSION = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PreviousSession'
X509 = 'urn:oasis:names:tc:SAML:2.0:ac:classes:X509'
PGP = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PGP'
SPKI = 'urn:oasis:names:tc:SAML:2.0:ac:classes:SPKI'
XMLDSIG = 'urn:oasis:names:tc:SAML:2.0:ac:classes:XMLDSig'
SMARTCARD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Smartcard'
SOFTWARE_PKI = 'urn:oasis:names:tc:SAML:2.0:ac:classes:SoftwarePKI'
TELEPHONY = 'urn:oasis:names:tc:SAML:2.0:ac:classes:Telephony'
NOMAD_TELEPHONY = 'urn:oasis:names:tc:SAML:2.0:ac:classes:NomadTelephony'
PERSONAL_TELEPHONY = 'urn:oasis:names:tc:SAML:2.0:ac:classes:PersonalTelephony'
AUTHENTICATED_TELEPHONY = 'urn:oasis:names:tc:SAML:2.0:ac:classes:AuthenticatedTelephony'
SECURED_REMOTE_PASSWORD = 'urn:oasis:names:tc:SAML:2.0:ac:classes:SecureRemotePassword'
TLS_CLIENT = 'urn:oasis:names:tc:SAML:2.0:ac:classes:TLSClient'
TIME_SYNC_TOKEN = 'urn:oasis:names:tc:SAML:2.0:ac:classes:TimeSyncToken'
ALL_CLASS_REFS = [UNSPECIFIED,
PASSWORD_PROTECTED,
MOBILE_TWO_FACTOR_UNREGISTERED,
MOBILE_TWO_FACTOR_CONTRACT,
MOBILE_SMARTCARD_PKI,
INTERNET_PROTOCOL,
INTERNET_PROTOCOL_PASSWORD,
KERBEROS,
MOBILE_ONE_FACTOR_UNREGISTERED,
MOBILE_ONE_FACTOR_CONTRACT,
PASSWORD,
PREVIOUS_SESSION,
X509,
PGP,
SPKI,
XMLDSIG,
SMARTCARD,
SOFTWARE_PKI,
TELEPHONY,
NOMAD_TELEPHONY,
PERSONAL_TELEPHONY,
AUTHENTICATED_TELEPHONY,
SECURED_REMOTE_PASSWORD,
TLS_CLIENT,
TIME_SYNC_TOKEN]
ORDERED_CLASS_REFS = ALL_CLASS_REFS
end
module ComplexTypes
require 'saml/complex_types/role_descriptor_type'
require 'saml/complex_types/request_abstract_type'
require 'saml/complex_types/status_response_type'
require 'saml/complex_types/endpoint_type'
require 'saml/complex_types/indexed_endpoint_type'
require 'saml/complex_types/sso_descriptor_type'
require 'saml/complex_types/attribute_type'
require 'saml/complex_types/localized_name_type'
require 'saml/complex_types/statement_abstract_type'
require 'saml/complex_types/subject_query_abstract_type'
require 'saml/complex_types/attribute_query_type'
require 'saml/complex_types/evidence_type'
require 'saml/complex_types/advice_type'
end
module Elements
require 'saml/elements/signature'
require 'saml/elements/authenticating_authority'
require 'saml/elements/subject_locality'
require 'saml/elements/authn_context'
require 'saml/elements/audience'
require 'saml/elements/audience_restriction'
require 'saml/elements/sub_status_code'
require 'saml/elements/status_code'
require 'saml/elements/status_detail'
require 'saml/elements/status'
require 'saml/elements/subject_confirmation_data'
require 'saml/elements/subject_confirmation'
require 'saml/elements/encrypted_assertion'
require 'saml/elements/encrypted_attribute'
require 'saml/elements/name_id'
require 'saml/elements/name_id_format'
require 'saml/elements/session_index'
require 'saml/elements/encrypted_id'
require 'saml/elements/attribute_value'
require 'saml/elements/attribute'
require 'saml/elements/attribute_statement'
require 'saml/elements/entity_attributes'
require 'saml/elements/publication_info'
require 'saml/elements/md_extensions'
require 'saml/elements/samlp_extensions'
require 'saml/elements/service_name'
require 'saml/elements/service_description'
require 'saml/elements/requested_attribute'
require 'saml/elements/attribute_consuming_service'
require 'saml/elements/subject'
require 'saml/elements/conditions'
require 'saml/elements/authn_statement'
require 'saml/elements/requested_authn_context'
require 'saml/elements/key_descriptor'
require 'saml/elements/organization_name'
require 'saml/elements/organization_display_name'
require 'saml/elements/organization_url'
require 'saml/elements/organization'
require 'saml/elements/contact_person'
require 'saml/elements/advice'
require 'saml/elements/idp_sso_descriptor'
require 'saml/elements/sp_sso_descriptor'
require 'saml/elements/attribute_authority_descriptor'
require 'saml/elements/entity_descriptor'
require 'saml/elements/entities_descriptor'
require 'saml/elements/attribute_query'
require 'saml/elements/evidence'
require 'saml/elements/idp_entry'
require 'saml/elements/idp_list'
require 'saml/elements/scoping'
require 'saml/elements/name_id_policy'
end
module Rails
require 'saml/rails/controller_helper'
end
require 'saml/assertion'
require 'saml/authn_request'
require 'saml/artifact'
require 'saml/response'
require 'saml/artifact_resolve'
require 'saml/artifact_response'
require 'saml/logout_request'
require 'saml/logout_response'
require 'saml/provider'
require 'saml/basic_provider'
require 'saml/null_provider'
module ProviderStores
require 'saml/provider_stores/file'
require 'saml/provider_stores/url'
end
module ProtocolBinding
HTTP_ARTIFACT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact'
HTTP_POST = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST'
HTTP_REDIRECT = 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect'
SOAP = 'urn:oasis:names:tc:SAML:2.0:bindings:SOAP'
end
def self.current_provider
Thread.current['saml_current_provider'] || NullProvider.new
end
def self.current_provider=(provider)
Thread.current['saml_current_provider'] = provider
end
def self.current_store
store_name = Thread.current['saml_current_store']
Saml::Config.registered_stores[store_name] ||
Saml::Config.registered_stores[Saml::Config.default_store] ||
raise(Errors::InvalidStore.new(store_name))
end
def self.current_store=(store_name)
Thread.current['saml_current_store'] = store_name
end
def self.setup
yield Saml::Config
end
def self.generate_id
"_#{::SecureRandom.hex(20)}"
end
def self.provider(entity_id)
if current_provider && current_provider.entity_id == entity_id
current_provider
else
current_store.find_by_entity_id(entity_id) || raise(Saml::Errors::InvalidProvider.new("Cannot find provider with entity_id: #{entity_id}"))
end
end
def self.parse_message(message, type)
if %w(authn_request response logout_request logout_response artifact_resolve artifact_response).include?(type.to_s)
klass = "Saml::#{type.to_s.camelize}".constantize
klass.parse(message, single: true)
elsif klass = type.to_s.camelize.safe_constantize
klass.parse(message, single: true)
else
nil
end
end
end
require 'saml/config'