fresh-auth 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ redeploy
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fresh-auth.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Isaac Betesh
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,106 @@
1
+ # Fresh::Auth
2
+
3
+ This gem makes it really, REALLY easy to use the Freshbooks API. It couldn't be easier.
4
+
5
+ With only 3 functions you'll ever need to use, and only 2 required configuration values, it can't get any easier.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+ gem 'fresh-auth'
11
+ And then execute:
12
+ $ bundle
13
+ Or install it yourself as:
14
+ $ gem install fresh-auth
15
+
16
+ ## Usage
17
+
18
+ ### Configuration:
19
+
20
+ You must define your Freshbooks subdomain and your OAuth Secret in your application code before using Fresh::Auth. For Ruby on Rails apps, a new file at config/initializers/fresh-auth.rb would be appropriate.
21
+
22
+ Your configuration file should look like this (you fill in the three empty strings):
23
+
24
+ Fresh::Auth.configure do |config|
25
+
26
+ # The part of your login url between 'http://' and '.freshbooks.com'
27
+ config.url.subdomain = ""
28
+
29
+ # Under 'My Account' (on the top right when you're logged into Freshbooks)
30
+ # -> 'Freshbooks API' -> 'OAuth Developer Access' -> 'OAuth Secret'
31
+ # You'll need to request this from Freshbooks initially.
32
+ config.oauth_secret = ""
33
+
34
+ # Optional. Any string of your choice. Be creative or check out http://www.thebitmill.com/tools/password.html
35
+ config.nonce_salt = ""
36
+ end
37
+
38
+ Fear not: If you try to use Fresh::Auth without configuring it first, an exception will be thrown that clearly describes the problem.
39
+
40
+ ### Public API:
41
+
42
+ There are two modules in this API: Fresh::Auth::Authentication and Fresh::Auth::Api
43
+
44
+ #### Fresh::Auth::Authentication
45
+
46
+ This module authenticates you with Freshbooks, storing the authentication in an array called `session`. This integrates seamlessly with Ruby on Rails' controller environment. If you're using some framework other than Ruby on Rails, make sure to define session in your class before including the Authentication module. This isn't recommended because your class will also need to define other objects called `params` and `request` and implement a `redirect_to` method. It gets complicated. Better leave it to Rails to handle this for you.
47
+
48
+ The only public function of this module is AuthenticateWithFreshbooks.
49
+
50
+ To use it, just add the following line of code to your controller:
51
+ `
52
+ include Fresh::Auth::Authentication
53
+ `
54
+
55
+ Then, the following line of code authenticates with Freshbooks from any method in your controller:
56
+ `
57
+ AuthenticateWithFreshbooks()
58
+ `
59
+
60
+ Note that, after authenticating with Freshbooks, the user will be redirected back to the same path using HTTP GET, so make sure the resource supports HTTP GET and that in the business logic executed on GET, AuthenticateWihFreshbooks() is called.
61
+
62
+ #### Fresh::Auth::Api
63
+
64
+ Once you've authenticated, you want to send XML requests to Freshbooks. The first step is preparing the XML with Fresh::Auth::Api.GenerateXml, which you'll supply with a block that defines all the nested XML that you want in your request. GenerateXml also takes two arguments before the block: the class and method that you want to call.
65
+
66
+ First, in your controller:
67
+ `include Fresh::Auth::Api`
68
+
69
+ Then, in some method in that controller:
70
+
71
+ my_xml = GenerateXml :invoice, :update do |xml|
72
+ xml.client_id 20
73
+ xml.status 'sent'
74
+ xml.notes 'Pick up the car by 5'
75
+ xml.terms 'Cash only'
76
+ xml.lines {
77
+ xml.line {
78
+ xml.name 'catalytic converter'
79
+ xml.quantity 1
80
+ xml.unit_cost 450
81
+ xml.type 'Item'
82
+ }
83
+ xml.line {
84
+ xml.name 'labor'
85
+ xml.quantity 1
86
+ xml.unit_cost 60
87
+ xml.type 'Time'
88
+ }
89
+ }
90
+ end
91
+
92
+ Ok, you created the XML. Now you want to send it. Sounds pretty complicated, right? Not at all! Ready? Let's go!
93
+
94
+ `_response = PostToFreshbooksApi my_xml`
95
+
96
+ Now, are you wondering what's in `_response`? I'll tell you shortly, but before we discuss that, we have to know about the exception that PostToFreshbooksApi might raise. It raises a detailed error message if the response status is not 'ok'. Makes sense, right?
97
+
98
+ Now, you still want to know what's in `_response`? Oh, nothing fancy. Just a Nokogiri XML object, representing the root element of the xml response. Could this get any easier?
99
+
100
+ ## Contributing
101
+
102
+ 1. Fork it
103
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
104
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
105
+ 4. Push to the branch (`git push origin my-new-feature`)
106
+ 5. Create new Pull Request
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/fresh/auth/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Isaac Betesh"]
6
+ gem.email = ["iybetesh@gmail.com"]
7
+ gem.description = `cat README.md`
8
+ gem.summary = "Authenticates with the Freshbooks API and stores the authentication in a session with minimal configuration"
9
+
10
+ gem.files = `git ls-files`.split($\)
11
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
12
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
13
+ gem.name = "fresh-auth"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = Fresh::Auth::VERSION
16
+ gem.license = 'MIT'
17
+ gem.add_runtime_dependency 'builder', '>= 3.0.0'
18
+ gem.add_runtime_dependency 'rest-client', '>= 1.6.7'
19
+ gem.add_runtime_dependency 'nokogiri', '>= 1.5.5'
20
+ end
@@ -0,0 +1 @@
1
+ require 'fresh/auth'
@@ -0,0 +1,3 @@
1
+ require "fresh/auth/version"
2
+ require 'fresh/auth/authentication'
3
+ require 'fresh/auth/api'
@@ -0,0 +1,37 @@
1
+ require 'fresh/auth/configuration'
2
+ require 'fresh/auth/constants'
3
+ require 'fresh/auth/parameters'
4
+ require 'rest_client'
5
+ require 'builder'
6
+ require 'nokogiri'
7
+
8
+ module Fresh
9
+ module Auth
10
+ module Api
11
+ def GenerateXml klass, method, &block
12
+ xml = Builder::XmlMarkup.new( :indent => 2 )
13
+ xml.instruct! :xml, :encoding => "utf-8"
14
+ xml.request :method => "#{klass}.#{method}" do |req|
15
+ yield xml
16
+ end
17
+ end
18
+
19
+ def PostToFreshbooksApi xml
20
+ root = Nokogiri::XML(RestClient.post Fresh::Auth.configuration.url.api, xml, HttpHeaders()).root
21
+ raise "Request to Freshbooks API failed:\n#{root}" if "ok" != root.attributes["status"].to_s
22
+ root
23
+ end
24
+ private
25
+ def HttpHeaders
26
+ _header = {
27
+ :'OAuth realm' => "",
28
+ Key::AUTH_TOKEN => session[Key::SESSION][Key::AUTH_TOKEN]
29
+ }.merge Parameters.Common()
30
+ _header[Key::SIGNATURE] += session[Key::SESSION][Key::AUTH_SECRET]
31
+ val = ""
32
+ _header.collect{ |k, v| val += "#{k}=\"#{v}\","}
33
+ { :Authorization => val.chomp(",") }
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,41 @@
1
+ require 'fresh/auth/configuration'
2
+ require 'fresh/auth/constants'
3
+ require 'fresh/auth/parameters'
4
+
5
+ module Fresh
6
+ module Auth
7
+ module Authentication
8
+
9
+ def AuthenticateWithFreshbooks
10
+ @redirect_url = request.url.split("?")[0]
11
+ if !session.has_key? Key::SESSION
12
+ Request() and return if !params.has_key? Key::VERIFIER
13
+ Access() and redirect_to @redirect_url
14
+ end
15
+ end
16
+
17
+ private
18
+ def ParseHttpResponse _response
19
+ _hash = {}
20
+ _response.split('&').collect{ |v|
21
+ elem = v.split('=')
22
+ { elem[0] => elem[1] }
23
+ }.each { |k| _hash.merge! k}
24
+ _hash
25
+ end
26
+
27
+ def Request
28
+ _response = ParseHttpResponse RestClient.post(Fresh::Auth.configuration.url.request, Parameters.Request(@redirect_url))
29
+ redirect_to "#{Fresh::Auth.configuration.url.auth}?#{Key::AUTH_TOKEN}=#{_response[Key::AUTH_TOKEN]}"
30
+ end
31
+
32
+ def Access
33
+ _response = ParseHttpResponse RestClient.post(Fresh::Auth.configuration.url.access, Parameters.Access(params))
34
+ session[Key::SESSION] = {
35
+ Key::AUTH_TOKEN => _response[Key::AUTH_TOKEN],
36
+ Key::AUTH_SECRET => _response[Key::AUTH_SECRET]
37
+ }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,69 @@
1
+ require 'fresh/auth/constants'
2
+
3
+ module Fresh
4
+ module Auth
5
+ class Configuration
6
+ attr_writer :oauth_secret
7
+ attr_accessor :url, :nonce_salt
8
+
9
+ class Url
10
+ attr_writer :subdomain
11
+
12
+ def subdomain
13
+ raise "Fresh::Auth.configuration.url.subdomain cannot be blank" if @subdomain.blank?
14
+ @subdomain
15
+ end
16
+
17
+ def request
18
+ base + "oauth/oauth_request.php"
19
+ end
20
+
21
+ def auth
22
+ base + "oauth/oauth_authorize.php"
23
+ end
24
+
25
+ def access
26
+ base + "oauth/oauth_access.php"
27
+ end
28
+
29
+ def api
30
+ base + "api/2.1/xml-in"
31
+ end
32
+ private
33
+ def base
34
+ "https://#{@subdomain}.freshbooks.com/"
35
+ end
36
+ end
37
+
38
+ def initialize
39
+ @url = Url.new
40
+ @nonce_salt = ''
41
+ end
42
+
43
+ def oauth_secret
44
+ raise "Fresh::Auth.configuration.oauth_secret cannot be blank" if @oauth_secret.blank?
45
+ @oauth_secret
46
+ end
47
+ end
48
+
49
+ class << self
50
+ def configuration
51
+ @configuration ||= Configuration.new
52
+ end
53
+
54
+ def constant_params
55
+ {
56
+ :oauth_consumer_key => @configuration.url.subdomain,
57
+ :oauth_signature_method => "PLAINTEXT",
58
+ Key::SIGNATURE => @configuration.oauth_secret+'&',
59
+ :oauth_version => 1.0
60
+ }
61
+ end
62
+ end
63
+
64
+ def self.configure
65
+ self.configuration ||= Configuration.new
66
+ yield configuration
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,11 @@
1
+ module Fresh
2
+ module Auth
3
+ module Key
4
+ SIGNATURE = "oauth_signature"
5
+ AUTH_TOKEN = "oauth_token"
6
+ AUTH_SECRET = "oauth_token_secret"
7
+ VERIFIER = "oauth_verifier"
8
+ SESSION = "freshbooks_oauth"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ require 'fresh/auth/configuration'
2
+ require 'fresh/auth/constants'
3
+
4
+ module Fresh
5
+ module Auth
6
+ module Parameters
7
+ def Parameters.ANewNonce
8
+ Digest::SHA2.hexdigest(Time.now.to_i.to_s + Fresh::Auth.configuration.nonce_salt + Random.new.rand.to_s)[0,20]
9
+ end
10
+
11
+ def Parameters.Common
12
+ { :oauth_timestamp => Time.now.to_i, :oauth_nonce => ANewNonce() }.merge Fresh::Auth.constant_params()
13
+ end
14
+
15
+ def Parameters.Request _redirect_url
16
+ { :oauth_callback => _redirect_url }.merge Common()
17
+ end
18
+
19
+ def Parameters.Access params
20
+ params.keep_if { |k, v| [Key::AUTH_TOKEN, Key::VERIFIER].include? k }.merge Common()
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,5 @@
1
+ module Fresh
2
+ module Auth
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fresh-auth
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Isaac Betesh
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: builder
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rest-client
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.6.7
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.6.7
46
+ - !ruby/object:Gem::Dependency
47
+ name: nokogiri
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.5.5
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.5.5
62
+ description: ! "# Fresh::Auth\n\nThis gem makes it really, REALLY easy to use the
63
+ Freshbooks API. It couldn't be easier.\n\nWith only 3 functions you'll ever need
64
+ to use, and only 2 required configuration values, it can't get any easier.\n\n##
65
+ Installation\n\nAdd this line to your application's Gemfile:\n gem 'fresh-auth'\nAnd
66
+ then execute:\n $ bundle\nOr install it yourself as:\n $ gem install fresh-auth\n\n##
67
+ Usage\n\n### Configuration:\n\nYou must define your Freshbooks subdomain and your
68
+ OAuth Secret in your application code before using Fresh::Auth. For Ruby on Rails
69
+ apps, a new file at config/initializers/fresh-auth.rb would be appropriate.\n\nYour
70
+ configuration file should look like this (you fill in the three empty strings):\n\n
71
+ \ Fresh::Auth.configure do |config|\n\n # The part of your login url between
72
+ 'http://' and '.freshbooks.com'\n config.url.subdomain = \"\"\n\n # Under
73
+ 'My Account' (on the top right when you're logged into Freshbooks)\n # ->
74
+ 'Freshbooks API' -> 'OAuth Developer Access' -> 'OAuth Secret'\n # You'll need
75
+ to request this from Freshbooks initially.\n config.oauth_secret = \"\"\n\n
76
+ \ # Optional. Any string of your choice. Be creative or check out http://www.thebitmill.com/tools/password.html\n
77
+ \ config.nonce_salt = \"\"\n end\n\nFear not: If you try to use Fresh::Auth
78
+ without configuring it first, an exception will be thrown that clearly describes
79
+ the problem.\n\n### Public API:\n\nThere are two modules in this API: Fresh::Auth::Authentication
80
+ and Fresh::Auth::Api\n\n#### Fresh::Auth::Authentication\n\nThis module authenticates
81
+ you with Freshbooks, storing the authentication in an array called `session`. This
82
+ integrates seamlessly with Ruby on Rails' controller environment. If you're using
83
+ some framework other than Ruby on Rails, make sure to define session in your class
84
+ before including the Authentication module. This isn't recommended because your
85
+ class will also need to define other objects called `params` and `request` and implement
86
+ a `redirect_to` method. It gets complicated. Better leave it to Rails to handle
87
+ this for you.\n\nThe only public function of this module is AuthenticateWithFreshbooks.\n\nTo
88
+ use it, just add the following line of code to your controller:\n`\ninclude Fresh::Auth::Authentication\n`\n\nThen,
89
+ the following line of code authenticates with Freshbooks from any method in your
90
+ controller:\n`\nAuthenticateWithFreshbooks()\n`\n\nNote that, after authenticating
91
+ with Freshbooks, the user will be redirected back to the same path using HTTP GET,
92
+ so make sure the resource supports HTTP GET and that in the business logic executed
93
+ on GET, AuthenticateWihFreshbooks() is called.\n\n#### Fresh::Auth::Api\n\nOnce
94
+ you've authenticated, you want to send XML requests to Freshbooks. The first step
95
+ is preparing the XML with Fresh::Auth::Api.GenerateXml, which you'll supply with
96
+ a block that defines all the nested XML that you want in your request. GenerateXml
97
+ also takes two arguments before the block: the class and method that you want to
98
+ call.\n\nFirst, in your controller:\n`include Fresh::Auth::Api`\n\nThen, in some
99
+ method in that controller:\n\n my_xml = GenerateXml :invoice, :update do |xml|\n
100
+ \ xml.client_id 20\n xml.status 'sent'\n xml.notes 'Pick up the car
101
+ by 5'\n xml.terms 'Cash only'\n xml.lines {\n xml.line {\n xml.name
102
+ 'catalytic converter'\n xml.quantity 1\n xml.unit_cost 450\n xml.type
103
+ 'Item'\n }\n xml.line {\n xml.name 'labor'\n xml.quantity
104
+ 1\n xml.unit_cost 60\n xml.type 'Time'\n }\n }\n end\n\nOk,
105
+ you created the XML. Now you want to send it. Sounds pretty complicated, right?
106
+ \ Not at all! Ready? Let's go!\n\n`_response = PostToFreshbooksApi my_xml`\n\nNow,
107
+ are you wondering what's in `_response`? I'll tell you shortly, but before we discuss
108
+ that, we have to know about the exception that PostToFreshbooksApi might raise.
109
+ \ It raises a detailed error message if the response status is not 'ok'. Makes
110
+ sense, right?\n\nNow, you still want to know what's in `_response`? Oh, nothing
111
+ fancy. Just a Nokogiri XML object, representing the root element of the xml response.
112
+ \ Could this get any easier?\n\n## Contributing\n\n1. Fork it\n2. Create your feature
113
+ branch (`git checkout -b my-new-feature`)\n3. Commit your changes (`git commit -am
114
+ 'Added some feature'`)\n4. Push to the branch (`git push origin my-new-feature`)\n5.
115
+ Create new Pull Request\n"
116
+ email:
117
+ - iybetesh@gmail.com
118
+ executables: []
119
+ extensions: []
120
+ extra_rdoc_files: []
121
+ files:
122
+ - .gitignore
123
+ - Gemfile
124
+ - LICENSE
125
+ - README.md
126
+ - Rakefile
127
+ - fresh-auth.gemspec
128
+ - lib/fresh-auth.rb
129
+ - lib/fresh/auth.rb
130
+ - lib/fresh/auth/api.rb
131
+ - lib/fresh/auth/authentication.rb
132
+ - lib/fresh/auth/configuration.rb
133
+ - lib/fresh/auth/constants.rb
134
+ - lib/fresh/auth/parameters.rb
135
+ - lib/fresh/auth/version.rb
136
+ homepage:
137
+ licenses:
138
+ - MIT
139
+ post_install_message:
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ! '>='
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ! '>='
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 1.8.21
158
+ signing_key:
159
+ specification_version: 3
160
+ summary: Authenticates with the Freshbooks API and stores the authentication in a
161
+ session with minimal configuration
162
+ test_files: []