app_rail-airtable 0.2.6 → 0.2.10

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
  SHA256:
3
- metadata.gz: 0d893dd5ef7b42015a34fe0dd55738ef1296799ba40bd235c4fd1c84665d6048
4
- data.tar.gz: 0bd37ae820e88d1198cd7e82a0d8a965d893d0d4c8dc32bc33975f7f7a95cbcc
3
+ metadata.gz: 3e57f8d279b01f1a7672be43fce5dda36da9df13bc3152399646f5be729d522c
4
+ data.tar.gz: 7928368ae9d1b4edfd75c651af688be2aa0e7697f31fd5c87afb1a9e96e3945c
5
5
  SHA512:
6
- metadata.gz: 4a4fe2f30b7b4ef404062e5396d304abf584873b1bfb5bf32d81ed3f8d5f04a01172bac29600ed83f7e347ba28267e670ce083e5aa0b93e886357b00042bfe73
7
- data.tar.gz: 23cf52db82ae5ba37f9ea00751f867a93b7d1f800f480810b21e932e4cfec3792eede5a3569b3b0baba1143991d415e6a09c031bee7d981196d8ebcbfca2cfaf
6
+ metadata.gz: 5eef1c52f304ffe1b0050ff5a7278e47c825dce1e7e08a7af69550894070e23a465dc719fce7dddabfa06de9336f2b63d230685098d09a5c02711b17fe43394d
7
+ data.tar.gz: e3629a453d333d78aac608f45ac04b303b27c636f4350fb0f5c2b42c739756a274faabd55a08278b0cc445a5777f62ae57431acea477dae20575ff1cfac53e59
data/Gemfile.lock CHANGED
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- app_rail-airtable (0.2.6)
4
+ app_rail-airtable (0.2.10)
5
5
  activesupport
6
6
  airrecord
7
+ bcrypt
7
8
  sinatra
8
9
  thor
9
10
 
@@ -19,6 +20,7 @@ GEM
19
20
  airrecord (1.0.7)
20
21
  faraday (>= 0.10, < 2.0)
21
22
  net-http-persistent
23
+ bcrypt (3.1.16)
22
24
  byebug (11.1.3)
23
25
  concurrent-ruby (1.1.9)
24
26
  connection_pool (2.2.5)
data/README.md CHANGED
@@ -31,7 +31,9 @@ You should create a model class for each table you want to use in your App. Mode
31
31
  To provide support for routes, models can implement
32
32
  * ar_list_item (index)
33
33
  * ar_stack (show)
34
- * self.create_from_params (create)
34
+ * self.create_as_json (create)
35
+
36
+ In order to support authentication you should create a class (normally `User`) and inherit from `AppRail::Airtable::AuthenticationRecord`. Your table needs `Email`, `Password Hash` and `Access Token` columns.
35
37
 
36
38
  ### Servers
37
39
  You should create a single server which extends `AppRail::Airtable::Sinatra`, then add routes with the `resources` macro to add `index`, `show` and `create` routes. You can also provide your own routes.
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
24
24
  spec.add_dependency 'sinatra'
25
25
  spec.add_dependency 'airrecord'
26
26
  spec.add_dependency 'thor'
27
+ spec.add_dependency 'bcrypt'
27
28
 
28
29
  spec.add_development_dependency 'rspec'
29
30
  spec.add_development_dependency 'rack-test'
@@ -43,7 +43,7 @@ module AppRail
43
43
  # Customisable behaviour
44
44
 
45
45
  # Override to provide custom sorting or filtering for index
46
- def self.index
46
+ def self.index(user:)
47
47
  all
48
48
  end
49
49
 
@@ -0,0 +1,47 @@
1
+ require 'bcrypt'
2
+ require 'securerandom'
3
+
4
+ module AppRail
5
+ module Airtable
6
+ module Authenticatable
7
+ include BCrypt
8
+
9
+ def self.included(klass)
10
+ klass.extend(ClassMethods)
11
+ end
12
+
13
+ module ClassMethods
14
+ def create(email:, password:)
15
+ user = User.new("Email" => email, "Password Hash" => password_hash(password), "Access Token" => access_token)
16
+ user.create
17
+ user
18
+ end
19
+
20
+ def create_session_as_json(email:, password:)
21
+ user = find_by_email_and_password(email, password)
22
+ user&.oauth_session
23
+ end
24
+
25
+ def find_by_email_and_password(email, password)
26
+ all(filter: "AND({Email} = \"#{email}\",{Password Hash} = \"#{password_hash(password)}\")").first
27
+ end
28
+
29
+ def find_by_access_token(access_token)
30
+ all(filter: "{Access Token} = \"#{access_token}\"").first
31
+ end
32
+
33
+ def password_hash(password)
34
+ BCrypt::Password.create(password)
35
+ end
36
+
37
+ def access_token
38
+ SecureRandom.hex
39
+ end
40
+ end
41
+
42
+ def oauth_session
43
+ { access_token: self["Access Token"], scope: :user, refresh_token: "", token_type: :bearer, expires_in: 60000 }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ module AppRail
2
+ module Airtable
3
+ module AuthenticationHelpers
4
+ def current_user
5
+ @current_user ||= find_current_user
6
+ end
7
+
8
+ def find_current_user
9
+ authorization_header && bearer_token ? User.find_by_access_token(bearer_token) : nil
10
+ end
11
+
12
+ def bearer_token
13
+ authorization_values = authorization_header.split(" ")
14
+ return nil unless authorization_values.count > 1
15
+ authorization_values[1]
16
+ end
17
+
18
+ def authorization_header
19
+ request.env["HTTP_AUTHORIZATION"]
20
+ end
21
+
22
+ def authenticate!
23
+ halt 401 unless current_user
24
+ end
25
+ end
26
+ end
27
+ end
@@ -2,6 +2,7 @@ require 'sinatra'
2
2
  require 'json'
3
3
  require 'active_support'
4
4
 
5
+ # TODO: MBS - move to configure block or other
5
6
  Airrecord.api_key = ENV.fetch("AIRTABLE_API_KEY")
6
7
 
7
8
  class Symbol
@@ -29,33 +30,38 @@ module AppRail
29
30
  request.body.rewind
30
31
  JSON.parse(request.body.read)
31
32
  end
33
+
34
+ def params_and_body_as_json
35
+ request_body_as_json.merge(params)
36
+ end
32
37
  end
33
38
 
34
- def self.resources(name, only: [:index, :show, :create])
39
+ def self.resources(name, only: [:index, :show, :create], authenticated: false)
35
40
  only = [only] if only.is_a?(Symbol)
36
41
 
37
- index_route(name) if only.include?(:index)
38
- show_route(name) if only.include?(:show)
39
- create_route(name) if only.include?(:create)
42
+ index_route(name, authenticated) if only.include?(:index)
43
+ show_route(name, authenticated) if only.include?(:show)
44
+ create_route(name, authenticated) if only.include?(:create)
40
45
  end
41
46
 
42
- def self.index_route(name)
47
+ def self.index_route(name, authenticated)
43
48
  get "/#{name.to_s}" do
44
- name.classify_constantize.index.map(&:ar_list_item_as_json).to_json
49
+ authenticate! if authenticated
50
+ name.classify_constantize.index(user: authenticated ? current_user : nil).map(&:ar_list_item_as_json).to_json
45
51
  end
46
52
  end
47
53
 
48
- def self.show_route(name)
54
+ def self.show_route(name, authenticated)
49
55
  get "/#{name.to_s}/:id" do
56
+ authenticate! if authenticated
50
57
  name.classify_constantize.find(params['id']).ar_stack_as_json.to_json
51
58
  end
52
59
  end
53
60
 
54
- def self.create_route(name)
61
+ def self.create_route(name, authenticated)
55
62
  post "/#{name.to_s}" do
56
- request.body.rewind # in case someone already read it
57
- data = JSON.parse(request.body.read)
58
- as_json = name.classify_constantize.create_with_params_as_json(data.merge(params))
63
+ authenticate! if authenticated
64
+ as_json = name.classify_constantize.create_as_json(current_user: authenticated ? current_user : nil, params: params_and_body_as_json)
59
65
  [201, as_json.to_json]
60
66
  end
61
67
  end
@@ -1,5 +1,5 @@
1
1
  module AppRail
2
2
  module Airtable
3
- VERSION = "0.2.6"
3
+ VERSION = "0.2.10"
4
4
  end
5
5
  end
@@ -1,8 +1,10 @@
1
1
  require "app_rail/airtable/version"
2
+ require "app_rail/airtable/string_ext"
2
3
  require "app_rail/airtable/application_record"
4
+ require "app_rail/airtable/authenticatable"
5
+ require "app_rail/airtable/authentication_helpers"
3
6
  require "app_rail/airtable/sinatra"
4
7
  require "app_rail/airtable/generator"
5
- require "app_rail/airtable/string_ext"
6
8
 
7
9
  module AppRail
8
10
  module Airtable
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: app_rail-airtable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.6
4
+ version: 0.2.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Brooke-Smith
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-01 00:00:00.000000000 Z
11
+ date: 2021-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bcrypt
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rspec
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -116,6 +130,8 @@ files:
116
130
  - bin/setup
117
131
  - lib/app_rail/airtable.rb
118
132
  - lib/app_rail/airtable/application_record.rb
133
+ - lib/app_rail/airtable/authenticatable.rb
134
+ - lib/app_rail/airtable/authentication_helpers.rb
119
135
  - lib/app_rail/airtable/generator.rb
120
136
  - lib/app_rail/airtable/sinatra.rb
121
137
  - lib/app_rail/airtable/string_ext.rb