monk-id 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
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
@@ -0,0 +1 @@
1
+ lib/**/*.rb
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in monk-id.gemspec
4
+ gemspec
@@ -0,0 +1,40 @@
1
+ Monk ID Ruby
2
+ ============
3
+
4
+ Full API docs: http://monkdev.github.io/monk-id-ruby/Monk/Id.html
5
+
6
+ Add to your `Gemfile`:
7
+
8
+ ```ruby
9
+ gem 'monk-id', :github => 'MonkDev/monk-id-ruby', :branch => 'next'
10
+ ```
11
+
12
+ For Rails and Sinatra, copy `config/monk_id.sample.yml` in this repository to
13
+ `config/monkid.yml` in your app. This will be loaded automatically. All other
14
+ apps need to load their config explicitly:
15
+
16
+ ```ruby
17
+ Monk::Id.load_config('/path/to/monk_id.yml', 'development')
18
+ ```
19
+
20
+ Next, load the payload:
21
+
22
+ ```ruby
23
+ Monk::Id.load_payload(params[:monk_id_payload])
24
+ ```
25
+
26
+ Or, if using the `cookie` option, simply pass in a hash-like cookies object:
27
+
28
+ ```ruby
29
+ Monk::Id.load_payload(cookies)
30
+ ```
31
+
32
+ Then you can access the user's ID and email:
33
+
34
+ ```ruby
35
+ Monk::Id.user_id
36
+ Monk::Id.user_email
37
+ ```
38
+
39
+ `nil` is returned if the user isn't signed in or the payload can't be decoded
40
+ and verified.
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,11 @@
1
+ development:
2
+ app_id: YOUR_APP_ID
3
+ app_secret: YOUR_APP_SECRET
4
+
5
+ staging:
6
+ app_id: YOUR_APP_ID
7
+ app_secret: YOUR_APP_SECRET
8
+
9
+ production:
10
+ app_id: YOUR_APP_ID
11
+ app_secret: YOUR_APP_SECRET
@@ -0,0 +1,186 @@
1
+ require 'base64'
2
+ require 'json'
3
+ require 'openssl'
4
+ require 'yaml'
5
+
6
+ # Global Monk namespace.
7
+ module Monk
8
+ # Integrate Monk ID on the server-side by accessing payloads from the
9
+ # client-side JavaScript.
10
+ #
11
+ # @author Monk Development, Inc.
12
+ module Id
13
+ # Expected path of config file in Rails and Sinatra relative to the app's
14
+ # root directory.
15
+ CONFIG_FILE = 'config/monk_id.yml'.freeze
16
+
17
+ # Name of the cookie that (optionally) stores the payload.
18
+ COOKIE_NAME = '_monkIdPayload'.freeze
19
+
20
+ class << self
21
+ # Load a YAML config file for a specific environment. Rails and Sinatra
22
+ # apps don't need to call this method if the config file is stored at
23
+ # {CONFIG_FILE}, as it's loaded automatically.
24
+ #
25
+ # @param path [String] Path of YAML config file to load. Leave `nil` to
26
+ # read from environment's `MONK_ID_CONFIG` value.
27
+ # @param environment [String] Environment section to use. Leave `nil` to
28
+ # read from environment's `MONK_ID_ENV` value. Defaults to
29
+ # `development`.
30
+ # @raise [StandardError] If the file doesn't exist or can't be read.
31
+ # @return [Hash<String>] Loaded config values.
32
+ def load_config(path = nil, environment = nil)
33
+ if defined? Rails
34
+ path ||= File.join(Rails.root, CONFIG_FILE)
35
+ environment ||= Rails.env
36
+ elsif defined? Sinatra
37
+ path ||= File.join(Sinatra::Application.settings.root, CONFIG_FILE)
38
+ environment ||= Sinatra::Application.settings.environment.to_s
39
+ else
40
+ path ||= ENV['MONK_ID_CONFIG']
41
+ environment ||= ENV['MONK_ID_ENV'] || 'development'
42
+ end
43
+
44
+ config = YAML.load_file(path)[environment]
45
+
46
+ verify_config(config)
47
+
48
+ @@config = config
49
+ end
50
+
51
+ # Get a config value. Attempts to load the config if it hasn't already
52
+ # been loaded.
53
+ #
54
+ # @param key [String] Name of config value.
55
+ # @raise [StandardError] If the config can't be loaded.
56
+ # @return [*] Config value.
57
+ def config(key)
58
+ load_config unless @@config
59
+
60
+ @@config[key]
61
+ end
62
+
63
+ # Load a payload from the client-side.
64
+ #
65
+ # @param encoded_payload [String, #[]] Encoded payload or Hash-like
66
+ # cookies object to automatically load the payload from.
67
+ # @return [Hash<Symbol>] Decoded and verified payload. Empty if there's no
68
+ # payload or it fails verification.
69
+ def load_payload(encoded_payload = nil)
70
+ if encoded_payload.is_a? String
71
+ payload = encoded_payload
72
+ elsif encoded_payload.respond_to? :[]
73
+ payload = encoded_payload[COOKIE_NAME]
74
+ end
75
+
76
+ return @@payload = {} unless payload
77
+
78
+ begin
79
+ payload = decode_payload(payload)
80
+ verified = verify_payload(payload)
81
+ rescue
82
+ verified = false
83
+ end
84
+
85
+ @@payload = verified ? payload : {}
86
+ end
87
+
88
+ # Get the signed in user's UUID.
89
+ #
90
+ # @return [String] If signed in user.
91
+ # @return [nil] If no signed in user.
92
+ def user_id
93
+ payload_user :id
94
+ end
95
+
96
+ # Get the signed in user's email address.
97
+ #
98
+ # @return [String] If signed in user.
99
+ # @return [nil] If no signed in user.
100
+ def user_email
101
+ payload_user :email
102
+ end
103
+
104
+ # Check whether there's a signed in user.
105
+ #
106
+ # @return [Boolean] Whether there's a signed in user.
107
+ def signed_in?
108
+ !!user_id
109
+ end
110
+
111
+ protected
112
+
113
+ # Loaded config values.
114
+ @@config = nil
115
+
116
+ # Loaded payload.
117
+ @@payload = nil
118
+
119
+ # Verify that a config has all the required values.
120
+ #
121
+ # @param config [Hash<String>] Config values.
122
+ # @raise [RuntimeError] If invalid.
123
+ # @return [true] If valid.
124
+ def verify_config(config)
125
+ raise 'no config loaded' unless config
126
+ raise 'no `app_id` config value' unless config['app_id']
127
+ raise 'no `app_secret` config value' unless config['app_secret']
128
+
129
+ true
130
+ end
131
+
132
+ # Decode a payload from the client-side.
133
+ #
134
+ # @param encoded_payload [String] Encoded payload.
135
+ # @raise [JSON::ParserError] If invalid JSON.
136
+ # @return [Hash<Symbol>] Decoded payload.
137
+ def decode_payload(encoded_payload)
138
+ JSON.parse(Base64.decode64(encoded_payload), :symbolize_names => true)
139
+ end
140
+
141
+ # Generate the expected signature of a payload using the app's secret.
142
+ #
143
+ # @param payload [Hash<Symbol>] Decoded payload.
144
+ # @return [String] Expected signature of the payload.
145
+ def expected_signature(payload)
146
+ payload_clone = payload.clone
147
+ payload_clone[:user].delete(:signature)
148
+
149
+ OpenSSL::HMAC.digest(OpenSSL::Digest::SHA512.new, config('app_secret'), JSON.generate(payload_clone[:user]))
150
+ end
151
+
152
+ # Verify that a payload hasn't been tampered with or faked by comparing
153
+ # signatures.
154
+ #
155
+ # @param payload [Hash<Symbol>] Decoded payload.
156
+ # @return [Boolean] Whether the payload is legit.
157
+ def verify_payload(payload)
158
+ signature = Base64.decode64(payload[:user][:signature])
159
+
160
+ signature == expected_signature(payload)
161
+ end
162
+
163
+ # Get the loaded payload.
164
+ #
165
+ # @return [Hash<Symbol>] Loaded payload. Empty if there's no payload or it
166
+ # failed verification.
167
+ def payload
168
+ @@payload || load_payload
169
+ end
170
+
171
+ # Get a value from the `user` hash of the loaded payload.
172
+ #
173
+ # @param key [Symbol] Name of value.
174
+ # @return [*] Requested value or `nil` if not set.
175
+ def payload_user(key)
176
+ payload = self.payload
177
+
178
+ if payload.key?(:user)
179
+ payload[:user][key]
180
+ else
181
+ nil
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,6 @@
1
+ module Monk
2
+ module Id
3
+ # Current version of the library.
4
+ VERSION = '1.0.0'.freeze
5
+ end
6
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'monk/id/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'monk-id'
8
+ spec.version = Monk::Id::VERSION.dup
9
+ spec.authors = ['Monk Development, Inc.']
10
+ spec.email = ['support@monkdevelopment.com']
11
+ spec.description = 'Integrate Monk ID on the server-side by accessing payloads from the client-side JavaScript.'
12
+ spec.summary = spec.description
13
+ spec.homepage = 'https://github.com/MonkDev/monk-id-ruby'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.3'
22
+ spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'yard'
24
+ end
metadata ADDED
@@ -0,0 +1,106 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: monk-id
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Monk Development, Inc.
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-02-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: yard
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: Integrate Monk ID on the server-side by accessing payloads from the client-side
63
+ JavaScript.
64
+ email:
65
+ - support@monkdevelopment.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - .yardopts
72
+ - Gemfile
73
+ - README.md
74
+ - Rakefile
75
+ - config/monk_id.sample.yml
76
+ - lib/monk/id.rb
77
+ - lib/monk/id/version.rb
78
+ - monk-id.gemspec
79
+ homepage: https://github.com/MonkDev/monk-id-ruby
80
+ licenses:
81
+ - MIT
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 1.8.23
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Integrate Monk ID on the server-side by accessing payloads from the client-side
104
+ JavaScript.
105
+ test_files: []
106
+ has_rdoc: