Module: Jive::SignedRequest

Defined in:
lib/jive/signed_request.rb,
lib/jive/signed_request/version.rb

Overview

:nodoc:

Constant Summary collapse

VERSION =
"0.1.2"

Class Method Summary collapse

Class Method Details

.authenticate(authorization_header, client_secret) ⇒ Object

Authenticate an authorization header

Authenticates that an authorization header sent by Jive is valid given an apps secret

  • Args :

    • authorization_header -> the entire Authorization header sent by Jive

    • client_secret -> the client secret to authenticate the header with

  • Returns :

    • the signature

  • Raises :

    • ArgumentError -> if the authorization_header does not contain JiveEXTN

    • ArgumentError -> if the heauthorization_header does not contain all the required parameters

    • ArgumentError -> if the heauthorization_header has expired (more than 5 minutes old)



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/jive/signed_request.rb', line 55

def authenticate(authorization_header, client_secret)
	# Validate JiveEXTN part of header
	if !authorization_header.match(/^JiveEXTN/)
		raise ArgumentError, "Jive authorization header is not properly formatted, must start with JiveEXTN"
	end

	paramMap = ::CGI.parse authorization_header.gsub(/^JiveEXTN\s/,'')

	# Validate all parameters are passed from header
	if !paramMap.has_key?("algorithm") ||
		!paramMap.has_key?("client_id") ||
		!paramMap.has_key?("jive_url") ||
		!paramMap.has_key?("tenant_id") ||
		!paramMap.has_key?("timestamp") ||
		!paramMap.has_key?("signature")
		raise ArgumentError, "Jive authorization header is partial"
	end

	# Validate timestamp is still valid
	timestamp = Time.at(paramMap["timestamp"].first.to_i/1000)
	secondsPassed = Time.now - timestamp

	if secondsPassed < 0 || secondsPassed > (5*60)
		raise ArgumentError, "Jive authorization is rejected since it's #{ secondsPassed } seconds old (max. allowed is 5 minutes)"
	end

	self.sign(authorization_header.gsub(/^JiveEXTN\s/,'').gsub(/\&signature[^$]+/,''), client_secret) === paramMap["signature"].first
end

.sign(string, secret, algorithm = nil) ⇒ Object

Sign a string with a secret

Sign a string with a secret and get the signature

  • Args :

    • string -> the string to sign

    • secret -> the secret to use

  • Returns :

    • the signature

  • Raises :

    • ArgumentError -> if no algorithm passed and algorithm could not be derived from the string



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/jive/signed_request.rb', line 23

def sign(string, secret, algorithm = nil)
	plain = ::Base64.decode64(secret.gsub(/\.s$/,''))
	
	# if no override algorithm passed try and extract from string
	if algorithm.nil?
		paramMap = ::CGI.parse string

		if !paramMap.has_key?("algorithm")
			raise ArgumentError, "missing algorithm"
		end

		algorithm = paramMap["algorithm"].first.gsub(/^hmac/i,'')
	end
	
	hmac = ::OpenSSL::HMAC.digest(algorithm, plain, string)
	Base64::encode64(hmac).gsub(/\n$/,'')
end

.validate_registration(validationBlock, *args) ⇒ Object

Validates an app registration

Validates an app registration came from where it claims via jiveSignatureURL

  • Args :

    • validationBlock -> the request body of the registration

    • args -> additional arguments

  • Returns :

    • boolean



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/jive/signed_request.rb', line 94

def validate_registration(validationBlock, *args)
	options = ((args.last.is_a?(Hash)) ? args.pop : {})

	require "open-uri"
	require "net/http"
	require "openssl"

	jive_signature_url = validationBlock[:jiveSignatureURL]
	jive_signature = validationBlock[:jiveSignature]

	validationBlock.delete(:jiveSignature)

	if !validationBlock[:clientSecret].nil?
		validationBlock[:clientSecret] = Digest::SHA256.hexdigest(validationBlock[:clientSecret])
	end

	uri = URI.parse(jive_signature_url)
	http = Net::HTTP.new(uri.host, uri.port)
	http.use_ssl = true
	http.verify_mode = OpenSSL::SSL::VERIFY_NONE if http.use_ssl? && !options[:verify_ssl]

	buffer = ''
	validationBlock = validationBlock.sort

	(validationBlock.respond_to?(:to_h) ? validationBlock.to_h : Hash[validationBlock] ).each_pair { |k,v|
		buffer = "#{buffer}#{k}:#{v}\n"
	}

	request = Net::HTTP::Post.new(uri.request_uri)
	request.body = buffer

	request["X-Jive-MAC"] = jive_signature
	request["Content-Type"] = "application/json"

	response = http.request(request)

	(response.code.to_i === 204)
end