userbin 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eda8c2ec2217b83cb4943e8302c0844f7d72c158
4
- data.tar.gz: ad7583bd3627cd2898cd2f95a687393e8e48d2dd
3
+ metadata.gz: ffb023fc0244c53569a55984108c8f5e6a6d0376
4
+ data.tar.gz: 3edcb3bd8e9419fd0dbd4135044933b50c8888f6
5
5
  SHA512:
6
- metadata.gz: 888dea6e532ce801149b4f3ae95cf875835f5c5a61fbc81fe636983b266e7310097052c64d2b4c3dcccee0c5440ff79b2abaca0fd21c813feafd6328258da2a4
7
- data.tar.gz: b5abb4dc7fc8f4f5f8b9a3d4a89a6d409c31322ce7748996d093d2eb72b8260972c998f02cd7621824334c788d875d97a323d55051892f386f2330cf30e9c8ff
6
+ metadata.gz: b81972686bf51fa8f71b613de9dfb7def38cb4c86d2106d1e1e3568529ec9f54787adf10085ca471802147afc3737989b06ac389d584075852bdafc1de93c943
7
+ data.tar.gz: dd8e6bc2f5c2d6b69ee1c32df5d1683d6aa99dfe40e0f1a4d34510b9f459c6b1124a2611376d86d51c696d77c430a572aeb4e8bd0fc50b31e3da4c41759a60ee
data/README.md CHANGED
@@ -9,6 +9,10 @@
9
9
 
10
10
  <!-- Your users can now easily activate two-factor authentication, configure the level of security in terms of monitoring and notifications and take action on suspicious behaviour. These settings are available as a per-user security settings page which is easily customized to fit your current layout. -->
11
11
 
12
+ ### Using Devise?
13
+
14
+ If you're using [Devise](https://github.com/plataformatec/devise) for authentication, check out the **[Userbin extension for Devise](https://github.com/userbin/devise_userbin)** for an even easier integration.
15
+
12
16
  ## Getting started
13
17
 
14
18
  Add the `userbin` gem to your `Gemfile`
@@ -23,66 +27,152 @@ Install the gem
23
27
  bundle install
24
28
  ```
25
29
 
26
- Load and configure the library with your Userbin API secret.
30
+ Load and configure the library with your Userbin API secret in an initializer or similar
27
31
 
28
32
  ```ruby
29
33
  require 'userbin'
30
34
  Userbin.api_secret = "YOUR_API_SECRET"
31
35
  ```
32
36
 
33
- Initialize a Userbin client for every incoming HTTP request and add it to the environment so that it's accessible during the request lifetime.
34
-
35
- ```ruby
36
- env['userbin'] = Userbin::Client.new(request)
37
- ```
38
-
37
+ ## Monitor a user
39
38
 
39
+ First you'll need to **initialize a Userbin client** for every incoming HTTP request and add it to the environment so that it's accessible during the request lifetime.
40
40
 
41
- ## Monitor a user
41
+ To **monitor a logged in user**, simply call `authorize!` on the Userbin object. You need to pass the user id, and optionally a hash of user properties, preferrable including at least `email`. This call only result in an HTTP request once every 5 minutes.
42
42
 
43
- To monitor a logged in user, simply call `authorize!` on the Userbin object. You need to pass the user id, and optionally a hash of [user properties](.), preferrable including at least `email`. This call only result in an HTTP request once every 5 minutes.
43
+ ### 1. Authorize the current user
44
44
 
45
45
  ```ruby
46
- # do this for *every* request, right after current_user is assigned
47
- env['userbin'].authorize!(current_user.id, { email: current_user.email })
46
+ class ApplicationController < ActionController::Base
47
+ # Define a before filter which is run on all requests
48
+ before_filter :initialize_userbin
49
+
50
+ # Your controller code here
51
+
52
+ private
53
+ def initialize_userbin
54
+ # Initialize Userbin and add it to the request environment
55
+ env['userbin'] = Userbin::Client.new(request)
56
+
57
+ if current_user
58
+ # Optional details for text messages, emails and your dashboard
59
+ user_properties = {
60
+ email: current_user.email, # recommended
61
+ # Add `name`, `username` and `image` for improved experience
62
+ }
63
+
64
+ begin
65
+ # This checks against Userbin once every 5 minutes under the hood.
66
+ # The `id` MUST be unique across all your users and roles
67
+ env['userbin'].authorize!(current_user.id, user_properties)
68
+ rescue Userbin::Error
69
+ # Logged out from Userbin; clear your current_user and logout
70
+ # TODO: implement!
71
+ end
72
+ end
73
+ end
74
+ end
48
75
  ```
49
76
 
50
- Clear the session when the user logs out.
77
+ > **Verify that it works:** Log in to your Ruby application with an existing user, and [watch a user appear](https://dashboard.userbin.com/users) in your Userbin dashboard.
78
+
79
+ ### 2. Log out
80
+
81
+ As a last step, you'll need to **end the Userbin session** when the user logs out from your application.
51
82
 
52
83
  ```ruby
53
- env['userbin'].logout
54
- ```
84
+ def logout
85
+ # Your code for logging out a user
55
86
 
87
+ # End the Userbin session
88
+ env['userbin'].logout
89
+ end
90
+ ```
56
91
 
57
- Done! Now log in to your application and watch the user appear in your Userbin dashboard.
92
+ > **Verify that it works:** Log out of your Ruby application and watch the number of sessions for the user in your Userbin dashboard return to zero.
58
93
 
59
94
  ## Add a link to the user's security settings
60
95
 
61
- Create a new route where you redirect the user to its [security settings page](.), where they can configure two-factor authentication, revoke suspicious sessions and set up notifications.
96
+ Create a new route where you redirect the user to its security settings page, where they can configure two-factor authentication, revoke suspicious sessions and set up notifications.
62
97
 
63
98
  ```ruby
64
- redirect_to env['userbin'].security_settings_url
99
+ class UsersController < ApplicationController
100
+ def security_settings
101
+ redirect_to env['userbin'].security_settings_url
102
+ end
103
+ end
65
104
  ```
66
105
 
67
- ## Activate two-factor authentication
106
+ > **Verify that it works:** Log in to your Ruby application and visit your new route. This should redirect to https://security.userbin.com where you'll see that you have one active session. *Don't enable two-factor authentication just yet.*
107
+
108
+ ## Two-factor authentication
109
+
110
+
111
+ ### 1. Protect routes
68
112
 
69
113
  If the user has enabled two-factor authentication, `two_factor_authenticate!` will return the second factor that is used to authenticate. If SMS is used, this call will also send out an SMS to the user's registered phone number.
70
114
 
71
115
  ```ruby
72
- factor = env['userbin'].two_factor_authenticate!
73
-
74
- case factor
75
- when :authenticator then render 'authenticator_form'
76
- when :sms then render 'sms_form'
116
+ class UsersController < ApplicationController
117
+ before_filter :authenticate_with_userbin!
118
+
119
+ # Your controller code here
120
+
121
+ private
122
+ def authenticate_with_userbin!
123
+ begin
124
+ # Checks if two-factor authentication is needed. Returns nil if not.
125
+ factor = env['userbin'].two_factor_authenticate!
126
+
127
+ # Show form and message specific to the current factor
128
+ case factor
129
+ when :authenticator
130
+ redirect_to '/verify/authenticator'
131
+ when :sms
132
+ redirect_to '/verify/sms'
133
+ end
134
+ rescue Userbin::Error
135
+ # logged out from Userbin; clear your current_user and logout
136
+ end
137
+ end
77
138
  end
78
139
  ```
79
140
 
80
- The user enters the authentication code in the form and posts it to your handler.
141
+ > **Verify that it works:** Enable two-factor on your security settings page, followed by a logout and and login to your Ruby application. You should now be redirected to one of the routes in the case statement.
81
142
 
82
- ```ruby
83
- env['userbin'].two_factor_verify(params[:code])
143
+ ### 2. Show the two-factor authentication form to the user
144
+
145
+ ```html
146
+ <p>
147
+ Open the two-factor authentication app on your device to view your
148
+ authentication code and verify your identity.
149
+ </p>
150
+ <form action="/users/handle_two_factor_response" method="post">
151
+ <label for="code">Authentication code</label>
152
+ <input id="code" name="code" type="text" />
153
+ <input type="submit" value="Verify code" />
154
+ </form>
84
155
  ```
85
156
 
86
- ## Handling errors
157
+ ### 3. Verify the code from the user
158
+
159
+ The user enters the authentication code in the form and posts it to your handler.
87
160
 
88
- If any request runs into an subclass of `Userbin::Error` will be raised with more details on what went wrong.
161
+ ```ruby
162
+ def handle_two_factor_response
163
+ # Get the authentication code from the form
164
+ authentication_code = params[:code]
165
+
166
+ begin
167
+ env['userbin'].two_factor_verify(authentication_code)
168
+ rescue Userbin::UserUnauthorizedError
169
+ # invalid code, show the form again
170
+ rescue Userbin::ForbiddenError
171
+ # no tries remaining, log out
172
+ rescue Userbin::Error
173
+ # logged out from Userbin; clear your current_user and logout
174
+ end
175
+
176
+ # We made it through two-factor authentication!
177
+ end
178
+ ```
@@ -42,7 +42,7 @@ module Userbin
42
42
 
43
43
  @session_store.user_id = user_id
44
44
 
45
- if !session_token
45
+ unless session_token
46
46
  # Create a session, and implicitly a user with user_attrs
47
47
  session = Userbin::Session.post(
48
48
  "users/#{user_id}/sessions", user: user_attrs)
@@ -65,7 +65,8 @@ module Userbin
65
65
  # Destroy the current session specified in the session token
66
66
  begin
67
67
  Userbin::Session.destroy_existing('current')
68
- rescue Userbin::Error; end
68
+ rescue Userbin::Error # ignored
69
+ end
69
70
 
70
71
  # Clear the session token
71
72
  self.session_token = nil
@@ -112,5 +113,9 @@ module Userbin
112
113
  return session_token.challenge_type
113
114
  end
114
115
 
116
+ def authorized?
117
+ !!session_token
118
+ end
119
+
115
120
  end
116
121
  end
@@ -20,6 +20,12 @@ module Userbin
20
20
  end
21
21
 
22
22
  class Configuration
23
+ attr_accessor :request_timeout
24
+
25
+ def initialize
26
+ self.request_timeout = 2.0
27
+ end
28
+
23
29
  def api_secret
24
30
  ENV['USERBIN_API_SECRET'] || @_api_secret
25
31
  end
@@ -17,7 +17,7 @@ module Userbin
17
17
 
18
18
  def self.get_uname
19
19
  `uname -a 2>/dev/null`.strip if RUBY_PLATFORM =~ /linux|darwin/i
20
- rescue Errno::ENOMEM => ex # couldn't create subprocess
20
+ rescue Errno::ENOMEM # couldn't create subprocess
21
21
  "uname lookup failed"
22
22
  end
23
23
 
@@ -41,6 +41,21 @@ module Userbin
41
41
  end
42
42
  end
43
43
 
44
+ # Handle request errors
45
+ #
46
+ class RequestErrorHandler < Faraday::Middleware
47
+ def call(env)
48
+ env.request.timeout = Userbin.config.request_timeout
49
+ begin
50
+ @app.call(env)
51
+ rescue Faraday::ConnectionFailed
52
+ raise Userbin::RequestError, 'Could not connect to Userbin API'
53
+ rescue Faraday::TimeoutError
54
+ raise Userbin::RequestError, 'Userbin API timed out'
55
+ end
56
+ end
57
+ end
58
+
44
59
  # Adds details about current environment
45
60
  #
46
61
  class EnvironmentHeaders < Faraday::Middleware
@@ -48,7 +63,8 @@ module Userbin
48
63
  begin
49
64
  env[:request_headers]["X-Userbin-Client-User-Agent"] =
50
65
  MultiJson.encode(Userbin::Request.client_user_agent)
51
- rescue => error; end
66
+ rescue # ignored
67
+ end
52
68
 
53
69
  env[:request_headers]["User-Agent"] =
54
70
  "Userbin/v1 RubyBindings/#{Userbin::VERSION}"
@@ -98,36 +114,43 @@ module Userbin
98
114
  end
99
115
  end
100
116
 
101
- class JSONParser < Her::Middleware::DefaultParseJSON
102
- # This method is triggered when the response has been received. It modifies
103
- # the value of `env[:body]`.
104
- #
105
- # @param [Hash] env The response environment
106
- # @private
117
+ class JSONParser < Faraday::Response::Middleware
107
118
  def on_complete(env)
108
- env[:body] = '{}' if [204, 405].include?(env[:status])
109
- env[:body] = case env[:status]
119
+ response = if env[:body].nil? || env[:body].empty?
120
+ {}
121
+ else
122
+ begin
123
+ MultiJson.load(env[:body], :symbolize_keys => true)
124
+ rescue MultiJson::LoadError
125
+ raise Userbin::ApiError, 'Invalid response from Userbin API'
126
+ end
127
+ end
128
+
129
+ case env[:status]
130
+ when 201..299
131
+ # OK
132
+ when 400
133
+ raise Userbin::BadRequestError, response[:message]
134
+ when 401
135
+ raise Userbin::UnauthorizedError, response[:message]
110
136
  when 403
111
- raise Userbin::ForbiddenError.new(
112
- MultiJson.decode(env[:body])['message'])
137
+ raise Userbin::ForbiddenError, response[:message]
113
138
  when 404
114
- raise Userbin::NotFoundError.new(
115
- MultiJson.decode(env[:body])['message'])
139
+ raise Userbin::NotFoundError, response[:message]
116
140
  when 419
117
- raise Userbin::UserUnauthorizedError.new(
118
- MultiJson.decode(env[:body])['message'])
119
- when 400..599
120
- begin
121
- message = MultiJson.decode(env[:body])['message']
122
- raise Userbin::Error.new(message)
123
- rescue MultiJson::ParseError
124
- raise Userbin::ApiError.new
125
- end
141
+ raise Userbin::UserUnauthorizedError, response[:message]
142
+ when 422
143
+ raise Userbin::InvalidParametersError, response[:message]
126
144
  else
127
- parse(env[:body])
145
+ raise Userbin::ApiError, response[:message]
128
146
  end
147
+
148
+ env[:body] = {
149
+ data: response
150
+ }
129
151
  end
130
152
  end
153
+
131
154
  end
132
155
 
133
156
  end
@@ -25,7 +25,7 @@ module Userbin
25
25
  end
26
26
 
27
27
  def challenge_type
28
- @jwt.payload['chg']['typ'] if has_challenge?
28
+ @jwt.payload['chg']['typ'].to_sym if has_challenge?
29
29
  end
30
30
  end
31
31
  end
data/lib/userbin/utils.rb CHANGED
@@ -9,6 +9,7 @@ module Userbin
9
9
 
10
10
  Her::API.setup url: api_endpoint do |c|
11
11
  c.use Userbin::Request::Middleware::BasicAuth, api_secret
12
+ c.use Userbin::Request::Middleware::RequestErrorHandler
12
13
  c.use Userbin::Request::Middleware::EnvironmentHeaders
13
14
  c.use Userbin::Request::Middleware::ContextHeaders
14
15
  c.use Userbin::Request::Middleware::SessionToken
@@ -1,3 +1,3 @@
1
1
  module Userbin
2
- VERSION = "1.1.0"
2
+ VERSION = "1.1.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: userbin
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Johan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-09 00:00:00.000000000 Z
11
+ date: 2014-07-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: her