solidus_jwt 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +35 -0
  3. data/.gem_release.yml +5 -0
  4. data/.github/CODEOWNERS +4 -0
  5. data/.github/stale.yml +17 -0
  6. data/.gitignore +19 -0
  7. data/.rspec +1 -0
  8. data/.rubocop.yml +33 -0
  9. data/.ruby-gemset +1 -0
  10. data/.ruby-version +1 -0
  11. data/Gemfile +31 -0
  12. data/Rakefile +4 -28
  13. data/_config.yml +1 -0
  14. data/app/controllers/spree/api/oauths_controller.rb +12 -5
  15. data/app/decorators/solidus_jwt/spree/api/base_controller_decorator.rb +39 -0
  16. data/app/decorators/solidus_jwt/spree/user_decorator.rb +36 -0
  17. data/app/models/solidus_jwt/base_record.rb +11 -0
  18. data/app/models/solidus_jwt/token.rb +17 -4
  19. data/bin/console +17 -0
  20. data/bin/rails +18 -0
  21. data/bin/sandbox +81 -0
  22. data/bin/setup +8 -0
  23. data/config/locales/en.yml +2 -1
  24. data/db/migrate/20191212083655_add_foreign_key_to_users_table.rb +5 -0
  25. data/lib/generators/solidus_jwt/install/install_generator.rb +1 -11
  26. data/lib/solidus_jwt.rb +2 -0
  27. data/lib/solidus_jwt/concerns/decodeable.rb +5 -1
  28. data/lib/solidus_jwt/concerns/encodeable.rb +12 -2
  29. data/lib/solidus_jwt/devise_strategies/base.rb +23 -0
  30. data/lib/solidus_jwt/devise_strategies/password.rb +5 -11
  31. data/lib/solidus_jwt/devise_strategies/refresh_token.rb +7 -10
  32. data/lib/solidus_jwt/distributor/devise.rb +1 -1
  33. data/lib/solidus_jwt/engine.rb +6 -12
  34. data/lib/solidus_jwt/version.rb +1 -9
  35. data/solidus_jwt.gemspec +36 -0
  36. data/spec/lib/solidus_jwt/concerns/decodeable_spec.rb +0 -0
  37. data/spec/lib/solidus_jwt/concerns/encodeable_spec.rb +0 -0
  38. data/spec/lib/solidus_jwt/config_spec.rb +5 -0
  39. data/spec/lib/solidus_jwt/devise_strategies/password_spec.rb +76 -0
  40. data/spec/lib/solidus_jwt/devise_strategies/refresh_token_spec.rb +72 -0
  41. data/spec/lib/solidus_jwt/preferences_spec.rb +37 -0
  42. data/spec/lib/solidus_jwt_spec.rb +6 -0
  43. data/spec/models/solidus_jwt/token_spec.rb +41 -0
  44. data/spec/requests/spree/api/json_web_tokens_spec.rb +75 -0
  45. data/spec/requests/spree/api/oauths_spec.rb +120 -0
  46. data/spec/spec_helper.rb +24 -0
  47. data/spec/support/shared_examples/decodeable_examples.rb +21 -0
  48. data/spec/support/shared_examples/encodeable_examples.rb +27 -0
  49. metadata +65 -227
  50. data/app/assets/javascripts/spree/backend/solidus_jwt.js +0 -2
  51. data/app/assets/javascripts/spree/frontend/solidus_jwt.js +0 -2
  52. data/app/assets/stylesheets/spree/backend/solidus_jwt.css +0 -4
  53. data/app/assets/stylesheets/spree/frontend/solidus_jwt.css +0 -4
  54. data/app/controllers/spree/api/base_controller/json_web_tokens.rb +0 -22
  55. data/app/controllers/spree/api/base_controller_decorator.rb +0 -17
  56. data/app/models/application_record.rb +0 -3
  57. data/app/models/solidus_jwt/application_record.rb +0 -9
  58. data/app/models/spree/user_decorator.rb +0 -34
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 56581878cbe327640f9c05d72e030cbeeb0fc4c5c70d6d5d5d0a78a82fad388b
4
- data.tar.gz: fd8f5ca391a04a4cc5ab3cd53f1b626533fe3e040b238cd2a8c5fb25372874fa
3
+ metadata.gz: 0f3f9ec2c12488185b2ee9bff66409693f0d92ab52bde2adb67cc47df182a82a
4
+ data.tar.gz: a77951e23f7d2352c5a2dde5a2742997f38bfb8e07ac31f1ae4f8be7b6bfc12c
5
5
  SHA512:
6
- metadata.gz: 64a7e377baa923773260008a9940f6fb87239849ad0271dddab69479c74cf991b78f02c775362501dc1aefef4daca52aa01697db7b1ce5583d18fc0a074709f9
7
- data.tar.gz: 8c8dac8527c305d66d52cff3bad1bc6875746f5297493213d3db68f4ce72bcfa5b9ed6f8e95e7c5b787e402317d8cee1486ad78981b4e5768b156da9b816a1c7
6
+ metadata.gz: '058c6e5d8bcc54fa1b623b68ab46848f9e9ffb6feee2e97f048c1047ed6a5c11ce78990aef67b6432d051471e718c09ef00b30717b0419edd88d95cbf0ca8412'
7
+ data.tar.gz: 95993510aecd67280bc85dc283798edae88d9c8388c1997190f8420d2ea4351d6e88273d7f5236449df52ff035e276725c924a5a835144c10dc458146bbe6bb3
@@ -0,0 +1,35 @@
1
+ version: 2.1
2
+
3
+ orbs:
4
+ # Always take the latest version of the orb, this allows us to
5
+ # run specs against Solidus supported versions only without the need
6
+ # to change this configuration every time a Solidus version is released
7
+ # or goes EOL.
8
+ solidusio_extensions: solidusio/extensions@volatile
9
+
10
+ jobs:
11
+ run-specs-with-postgres:
12
+ executor: solidusio_extensions/postgres
13
+ steps:
14
+ - solidusio_extensions/run-tests
15
+ run-specs-with-mysql:
16
+ executor: solidusio_extensions/mysql
17
+ steps:
18
+ - solidusio_extensions/run-tests
19
+
20
+ workflows:
21
+ "Run specs on supported Solidus versions":
22
+ jobs:
23
+ - run-specs-with-postgres
24
+ - run-specs-with-mysql
25
+ "Weekly run specs against master":
26
+ triggers:
27
+ - schedule:
28
+ cron: "0 0 * * 4" # every Thursday
29
+ filters:
30
+ branches:
31
+ only:
32
+ - master
33
+ jobs:
34
+ - run-specs-with-postgres
35
+ - run-specs-with-mysql
data/.gem_release.yml ADDED
@@ -0,0 +1,5 @@
1
+ bump:
2
+ recurse: false
3
+ file: 'lib/solidus_jwt/version.rb'
4
+ message: Bump SolidusJwt to %{version}
5
+ branch: true
@@ -0,0 +1,4 @@
1
+ ##
2
+ # Default Code Owner
3
+ ##
4
+ * @skukx
data/.github/stale.yml ADDED
@@ -0,0 +1,17 @@
1
+ # Number of days of inactivity before an issue becomes stale
2
+ daysUntilStale: 60
3
+ # Number of days of inactivity before a stale issue is closed
4
+ daysUntilClose: 7
5
+ # Issues with these labels will never be considered stale
6
+ exemptLabels:
7
+ - pinned
8
+ - security
9
+ # Label to use when marking an issue as stale
10
+ staleLabel: wontfix
11
+ # Comment to post when marking an issue as stale. Set to `false` to disable
12
+ markComment: >
13
+ This issue has been automatically marked as stale because it has not had
14
+ recent activity. It will be closed if no further activity occurs. Thank you
15
+ for your contributions.
16
+ # Comment to post when closing a stale issue. Set to `false` to disable
17
+ closeComment: false
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ \#*
3
+ *~
4
+ .#*
5
+ .DS_Store
6
+ .idea
7
+ .project
8
+ .sass-cache
9
+ coverage
10
+ Gemfile.lock
11
+ tmp
12
+ nbproject
13
+ pkg
14
+ *.swp
15
+ spec/dummy
16
+ spec/examples.txt
17
+
18
+ sandbox/
19
+ .byebug_history
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,33 @@
1
+ require:
2
+ - solidus_dev_support/rubocop
3
+
4
+ AllCops:
5
+ Exclude:
6
+ - sandbox/**/*
7
+ - spec/dummy/**/*
8
+
9
+ Style/FrozenStringLiteralComment:
10
+ Enabled: false
11
+
12
+ Naming/PredicateName:
13
+ Exclude:
14
+ - app/decorators/solidus_kits/spree/stock/availability_validator_decorator.rb
15
+
16
+ Metrics/LineLength:
17
+ Enabled: false
18
+
19
+ Rails/SkipsModelValidations:
20
+ Exclude:
21
+ - 'spec/**/*'
22
+
23
+ RSpec/BeforeAfterAll:
24
+ Enabled: false
25
+
26
+ RSpec/ContextWording:
27
+ Enabled: false
28
+
29
+ RSpec/MultipleExpectations:
30
+ Enabled: false
31
+
32
+ RSpec/NestedGroups:
33
+ Enabled: false
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ solidus_jwt
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.6.5
data/Gemfile ADDED
@@ -0,0 +1,31 @@
1
+ source 'https://rubygems.org'
2
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
+
4
+ branch = ENV.fetch('SOLIDUS_BRANCH', 'master')
5
+ gem 'solidus', github: 'solidusio/solidus', branch: branch
6
+
7
+ # Needed to help Bundler figure out how to resolve dependencies,
8
+ # otherwise it takes forever to resolve them.
9
+ # See https://github.com/bundler/bundler/issues/6677
10
+ gem 'rails', '>0.a'
11
+
12
+ # Provides basic authentication functionality for testing parts of your engine
13
+ gem 'solidus_auth_devise'
14
+
15
+ case ENV['DB']
16
+ when 'mysql'
17
+ gem 'mysql2'
18
+ when 'postgresql'
19
+ gem 'pg'
20
+ else
21
+ gem 'sqlite3'
22
+ end
23
+
24
+ gemspec
25
+
26
+ # Use a local Gemfile to include development dependencies that might not be
27
+ # relevant for the project or for other contributors, e.g. pry-byebug.
28
+ #
29
+ # We use `send` instead of calling `eval_gemfile` to work around an issue with
30
+ # how Dependabot parses projects: https://github.com/dependabot/dependabot-core/issues/1658.
31
+ send(:eval_gemfile, 'Gemfile-local') if File.exist? 'Gemfile-local'
data/Rakefile CHANGED
@@ -1,30 +1,6 @@
1
- require 'bundler'
1
+ # frozen_string_literal: true
2
2
 
3
- Bundler::GemHelper.install_tasks
3
+ require 'solidus_dev_support/rake_tasks'
4
+ SolidusDevSupport::RakeTasks.install
4
5
 
5
- begin
6
- require 'spree/testing_support/extension_rake'
7
- require 'rubocop/rake_task'
8
- require 'rspec/core/rake_task'
9
-
10
- RSpec::Core::RakeTask.new(:spec)
11
-
12
- RuboCop::RakeTask.new
13
-
14
- task default: %i(first_run rubocop spec)
15
- rescue LoadError
16
- # no rspec available
17
- end
18
-
19
- task :first_run do
20
- if Dir['spec/dummy'].empty?
21
- Rake::Task[:test_app].invoke
22
- Dir.chdir('../../')
23
- end
24
- end
25
-
26
- desc 'Generates a dummy app for testing'
27
- task :test_app do
28
- ENV['LIB_NAME'] = 'solidus_jwt'
29
- Rake::Task['extension:test_app'].invoke
30
- end
6
+ task default: 'extension:specs'
data/_config.yml ADDED
@@ -0,0 +1 @@
1
+ theme: jekyll-theme-cayman
@@ -4,19 +4,26 @@ module Spree
4
4
  skip_before_action :authenticate_user
5
5
 
6
6
  def token
7
- if user = try_authenticate_user
8
- render_token_for(user)
7
+ result = catch(:warden) do
8
+ try_authenticate_user
9
+ end
10
+
11
+ case result
12
+ when Spree::User
13
+ render json: token_response_json(result)
14
+ when Hash
15
+ render status: :unauthorized, json: { error: I18n.t(result[:message], scope: 'devise.failure') }
9
16
  else
10
- render status: 401, json: { error: 'invalid username or password' }
17
+ render status: :unauthorized, json: { error: I18n.t(:invalid_credentials, scope: 'solidus_jwt') }
11
18
  end
12
19
  end
13
20
 
14
21
  private
15
22
 
16
- def render_token_for(user)
23
+ def token_response_json(user)
17
24
  expires_in = SolidusJwt::Config.jwt_expiration
18
25
 
19
- render json: {
26
+ {
20
27
  token_type: 'bearer',
21
28
  access_token: user.generate_jwt(expires_in: expires_in),
22
29
  expires_in: expires_in,
@@ -0,0 +1,39 @@
1
+ module SolidusJwt
2
+ module Spree
3
+ module Api
4
+ module BaseControllerDecorator
5
+ def self.prepended(base)
6
+ base.rescue_from JWT::DecodeError do
7
+ render "spree/api/errors/invalid_api_key", status: :unauthorized
8
+ end
9
+ end
10
+
11
+ ##
12
+ # Overrides Solidus
13
+ # @see https://github.com/solidusio/solidus/blob/master/api/app/controllers/spree/api/base_controller.rb
14
+ #
15
+ def load_user
16
+ return super if json_web_token.blank?
17
+
18
+ # rubocop:disable Naming/MemoizedInstanceVariableName
19
+ @current_api_user ||= ::Spree.user_class.find_by(id: json_web_token['id'])
20
+ # rubocop:enable Naming/MemoizedInstanceVariableName
21
+ end
22
+
23
+ def json_web_token
24
+ @json_web_token ||= SolidusJwt.decode(api_key).first
25
+ rescue JWT::DecodeError
26
+ # Allow spree to try and authenticate if we still allow it. Otherwise
27
+ # raise an error
28
+ return if SolidusJwt::Config.allow_spree_api_key
29
+
30
+ raise
31
+ end
32
+
33
+ if SolidusSupport.api_available?
34
+ ::Spree::Api::BaseController.prepend self
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,36 @@
1
+ module SolidusJwt
2
+ module Spree
3
+ module UserDecorator
4
+ def self.prepended(base)
5
+ base.has_many :auth_tokens, class_name: 'SolidusJwt::Token'
6
+ end
7
+
8
+ ##
9
+ # Generate a json web token
10
+ # @see https://github.com/jwt/ruby-jwt
11
+ # @return [String]
12
+ #
13
+ def generate_jwt(expires_in: nil)
14
+ SolidusJwt.encode(payload: as_jwt_payload, expires_in: expires_in)
15
+ end
16
+ alias generate_jwt_token generate_jwt
17
+
18
+ ##
19
+ # Serializes user attributes to hash and applies
20
+ # the sub jwt claim.
21
+ #
22
+ # @return [Hash] The payload for json web token
23
+ #
24
+ def as_jwt_payload
25
+ options = SolidusJwt::Config.jwt_options
26
+ claims = { sub: id }
27
+
28
+ as_json(options)
29
+ .merge(claims)
30
+ .as_json
31
+ end
32
+
33
+ ::Spree.user_class.prepend self
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,11 @@
1
+ module SolidusJwt
2
+ base_class = defined?(::ApplicationRecord) ? ::ApplicationRecord : ActiveRecord::Base
3
+
4
+ class BaseRecord < base_class
5
+ self.abstract_class = true
6
+
7
+ def self.table_name_prefix
8
+ 'solidus_jwt_'
9
+ end
10
+ end
11
+ end
@@ -1,9 +1,11 @@
1
1
  module SolidusJwt
2
- class Token < ApplicationRecord
2
+ class Token < BaseRecord
3
3
  attr_readonly :token
4
- enum auth_type: %i[refresh_token access_token]
4
+ enum auth_type: { refresh_token: 0, access_token: 1 }
5
5
 
6
- belongs_to :user, class_name: Spree::UserClassHandle.new
6
+ # rubocop:disable Rails/ReflectionClassName
7
+ belongs_to :user, class_name: ::Spree::UserClassHandle.new
8
+ # rubocop:enable Rails/ReflectionClassName
7
9
 
8
10
  scope :non_expired, -> {
9
11
  where(
@@ -12,7 +14,7 @@ module SolidusJwt
12
14
  )
13
15
  }
14
16
 
15
- enum auth_type: %i[refresh access]
17
+ enum auth_type: { refresh: 0, access: 1 }
16
18
 
17
19
  validates :token, presence: true
18
20
 
@@ -24,9 +26,11 @@ module SolidusJwt
24
26
  # Set all non expired refresh tokens to inactive
25
27
  #
26
28
  def self.invalidate(user)
29
+ # rubocop:disable Rails/SkipsModelValidations
27
30
  non_expired.
28
31
  where(user_id: user.to_param).
29
32
  update_all(active: false)
33
+ # rubocop:enable Rails/SkipsModelValidations
30
34
  end
31
35
 
32
36
  ##
@@ -37,10 +41,19 @@ module SolidusJwt
37
41
  non_expired.where(active: true).find_by(token: token).present?
38
42
  end
39
43
 
44
+ ##
45
+ # Whether the token should be honored.
46
+ # @return [Boolean] Will be true if the token is active and not expired.
47
+ # Otherwise false.
40
48
  def honor?
41
49
  active? && !expired?
42
50
  end
43
51
 
52
+ ##
53
+ # Whether the token is expired
54
+ # @return [Boolean] If the token is older than the configured refresh
55
+ # expiration amount then will be true. Otherwise false.
56
+ #
44
57
  def expired?
45
58
  created_at < SolidusJwt::Config.refresh_expiration.seconds.ago
46
59
  end
data/bin/console ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ require "bundler/setup"
6
+ require "solidus_jwt"
7
+
8
+ # You can add fixtures and/or initialization code here to make experimenting
9
+ # with your gem easier. You can also use a different console, if you like.
10
+ $LOAD_PATH.unshift(*Dir["#{__dir__}/../app/*"])
11
+
12
+ # (If you use this, don't forget to add pry to your Gemfile!)
13
+ # require "pry"
14
+ # Pry.start
15
+
16
+ require "irb"
17
+ IRB.start(__FILE__)
data/bin/rails ADDED
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # frozen_string_literal: true
4
+
5
+ app_root = 'sandbox'
6
+
7
+ unless File.exist? "#{app_root}/bin/rails"
8
+ warn 'Creating the sandbox app...'
9
+ Dir.chdir "#{__dir__}/.." do
10
+ system "#{__dir__}/sandbox" or begin # rubocop:disable Style/AndOr
11
+ warn 'Automatic creation of the sandbox app failed'
12
+ exit 1
13
+ end
14
+ end
15
+ end
16
+
17
+ Dir.chdir app_root
18
+ exec 'bin/rails', *ARGV
data/bin/sandbox ADDED
@@ -0,0 +1,81 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ case "$DB" in
6
+ postgres|postgresql)
7
+ RAILSDB="postgresql"
8
+ ;;
9
+ mysql)
10
+ RAILSDB="mysql"
11
+ ;;
12
+ sqlite|'')
13
+ RAILSDB="sqlite3"
14
+ ;;
15
+ *)
16
+ echo "Invalid DB specified: $DB"
17
+ exit 1
18
+ ;;
19
+ esac
20
+
21
+ if [ ! -z $SOLIDUS_BRANCH ]
22
+ then
23
+ BRANCH=$SOLIDUS_BRANCH
24
+ else
25
+ BRANCH="master"
26
+ fi
27
+
28
+ extension_name="solidus_jwt"
29
+
30
+ # Stay away from the bundler env of the containing extension.
31
+ function unbundled {
32
+ ruby -rbundler -e'b = proc {system *ARGV}; Bundler.respond_to?(:with_unbundled_env) ? Bundler.with_unbundled_env(&b) : Bundler.with_clean_env(&b)' -- $@
33
+ }
34
+
35
+ rm -rf ./sandbox
36
+ unbundled bundle exec rails new sandbox --database="$RAILSDB" \
37
+ --skip-bundle \
38
+ --skip-git \
39
+ --skip-keeps \
40
+ --skip-rc \
41
+ --skip-spring \
42
+ --skip-test \
43
+ --skip-javascript
44
+
45
+ if [ ! -d "sandbox" ]; then
46
+ echo 'sandbox rails application failed'
47
+ exit 1
48
+ fi
49
+
50
+ cd ./sandbox
51
+ cat <<RUBY >> Gemfile
52
+ gem 'solidus', github: 'solidusio/solidus', branch: '$BRANCH'
53
+ gem 'solidus_auth_devise', '>= 2.1.0'
54
+ gem 'rails-i18n'
55
+ gem 'solidus_i18n'
56
+ gem '$extension_name', path: '..'
57
+ group :test, :development do
58
+ platforms :mri do
59
+ gem 'pry-byebug'
60
+ end
61
+ end
62
+ RUBY
63
+
64
+ unbundled bundle install --gemfile Gemfile
65
+
66
+ unbundled bundle exec rake db:drop db:create
67
+
68
+ unbundled bundle exec rails generate spree:install \
69
+ --auto-accept \
70
+ --user_class=Spree::User \
71
+ --enforce_available_locales=true \
72
+ $@
73
+
74
+ unbundled bundle exec rails generate solidus:auth:install
75
+
76
+ echo
77
+ echo "🚀 Sandbox app successfully created for $extension_name!"
78
+ echo "🚀 Using $RAILSDB and Solidus $BRANCH"
79
+ echo "🚀 Use 'export DB=[postgres|mysql|sqlite]' to control the DB adapter"
80
+ echo "🚀 Use 'export SOLIDUS_BRANCH=<BRANCH-NAME>' to control the Solidus version"
81
+ echo "🚀 This app is intended for test purposes."