authentication-zero 2.9.2 → 2.9.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/lib/authentication_zero/version.rb +1 -1
  4. data/lib/generators/authentication/authentication_generator.rb +14 -67
  5. data/lib/generators/authentication/templates/controllers/api/application_controller.rb.tt +26 -0
  6. data/lib/generators/authentication/templates/controllers/html/application_controller.rb.tt +24 -0
  7. data/lib/generators/authentication/templates/models/model.rb.tt +2 -2
  8. data/lib/generators/authentication/templates/test_unit/application_system_test_case.rb.tt +15 -0
  9. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/email_verifications_controller_test.rb.tt +0 -4
  10. data/lib/generators/authentication/templates/test_unit/controllers/api/identity/emails_controller_test.rb.tt +0 -4
  11. data/lib/generators/authentication/templates/test_unit/controllers/api/passwords_controller_test.rb.tt +0 -4
  12. data/lib/generators/authentication/templates/test_unit/controllers/api/sessions/sudos_controller_test.rb.tt +0 -4
  13. data/lib/generators/authentication/templates/test_unit/controllers/api/sessions_controller_test.rb.tt +0 -4
  14. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/email_verifications_controller_test.rb.tt +0 -4
  15. data/lib/generators/authentication/templates/test_unit/controllers/html/identity/emails_controller_test.rb.tt +0 -4
  16. data/lib/generators/authentication/templates/test_unit/controllers/html/passwords_controller_test.rb.tt +0 -4
  17. data/lib/generators/authentication/templates/test_unit/controllers/html/sessions/sudos_controller_test.rb.tt +0 -4
  18. data/lib/generators/authentication/templates/test_unit/controllers/html/sessions_controller_test.rb.tt +0 -4
  19. data/lib/generators/authentication/templates/test_unit/system/identity/emails_test.rb.tt +0 -10
  20. data/lib/generators/authentication/templates/test_unit/system/passwords_test.rb.tt +0 -10
  21. data/lib/generators/authentication/templates/test_unit/system/sessions/sudos_test.rb.tt +0 -10
  22. data/lib/generators/authentication/templates/test_unit/system/sessions_test.rb.tt +0 -10
  23. data/lib/generators/authentication/templates/test_unit/test_helper.rb.tt +22 -0
  24. metadata +5 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1eb8eb320cf8198e9afebf59fd2365b2683e3b52840d8d455876a11b208e243c
4
- data.tar.gz: 3bc190759683e6e647a597bdb57e09e69243901a3390c401e09eac72ada96726
3
+ metadata.gz: d8173a1510dfbe78180ce29cbb83b5f79b84b0ed4ecacf0569344905c28f2a01
4
+ data.tar.gz: 9d89bc1c96a4b59b7c7bf2437bd038036e747f4e78c0a7d5a81f1c0ae4c86f28
5
5
  SHA512:
6
- metadata.gz: 0c2996c6cf6779c22442f7155025bb99a470f26a0dc23218aea96f413fd5ae85a1ffdebd796922b77a09f60a833c4a0bb72bc9e964f5c0695066cf4074c3f4e9
7
- data.tar.gz: 8cbf3eb8525e08fc10a5c00a88e1a82d928a107c969e7b6f4e65789d0db1d79b3a19369331f914b5baf439230a6b0e246287f6875f1f4ab7d57c4f470e96c0a0
6
+ metadata.gz: 34a5ed73cbd7f5e35cd9a1e16ae0e4880a677ffa94f3892c0c6292abb436b3fded01c4664dd5e77d5b8025718b60ea8507bdbd968243d94ef191980615b02ea4
7
+ data.tar.gz: 2afb2c4fbc2bef0c7e06fab12cf783f04c6bc811d7150ed58f4a73f430c23925f4e790c6e3477729e04f2ade59b80dd30220667952d7b41cc98a4106fc4e064e
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- authentication-zero (2.9.2)
4
+ authentication-zero (2.9.3)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -1,3 +1,3 @@
1
1
  module AuthenticationZero
2
- VERSION = "2.9.2"
2
+ VERSION = "2.9.3"
3
3
  end
@@ -32,12 +32,12 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
32
32
  end
33
33
 
34
34
  def create_configuration_files
35
- copy_file "config/redis/shared.yml", "config/redis/shared.yml" if options.lockable?
36
- copy_file "config/initializers/omniauth.rb", "config/initializers/omniauth.rb" if omniauthable?
35
+ copy_file "config/redis/shared.yml", "config/redis/shared.yml" if options.lockable?
36
+ copy_file "config/initializers/omniauth.rb", "config/initializers/omniauth.rb" if omniauthable?
37
37
  end
38
38
 
39
39
  def add_environment_configurations
40
- ratelimit_code = <<~CODE
40
+ ratelimit_code = <<~CODE
41
41
  # Rate limit general requests by IP address in a rate of 1000 requests per hour
42
42
  config.middleware.use(Rack::Ratelimit, name: "General", rate: [1000, 1.hour], redis: Redis.new, logger: Rails.logger) { |env| ActionDispatch::Request.new(env).ip }
43
43
  CODE
@@ -63,64 +63,9 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
63
63
  template "test_unit/fixtures.yml", "test/fixtures/#{fixture_file_name}.yml"
64
64
  end
65
65
 
66
- def add_application_controller_methods
67
- api_code = <<~CODE
68
- include ActionController::HttpAuthentication::Token::ControllerMethods
69
-
70
- before_action :set_current_request_details
71
- before_action :authenticate
72
-
73
- def require_sudo
74
- if Current.session.sudo_at < 30.minutes.ago
75
- render json: { error: "Enter your password to continue" }, status: :forbidden
76
- end
77
- end
78
-
79
- private
80
- def authenticate
81
- if session = authenticate_with_http_token { |token, _| Session.find_signed(token) }
82
- Current.session = session
83
- else
84
- request_http_token_authentication
85
- end
86
- end
87
-
88
- def set_current_request_details
89
- Current.user_agent = request.user_agent
90
- Current.ip_address = request.ip
91
- end
92
- CODE
93
-
94
- html_code = <<~CODE
95
- before_action :set_current_request_details
96
- before_action :authenticate
97
-
98
- def require_sudo
99
- if Current.session.sudo_at < 30.minutes.ago
100
- redirect_to new_sessions_sudo_path(proceed_to_url: request.url)
101
- end
102
- end
103
-
104
- private
105
- def authenticate
106
- if session = Session.find_by_id(cookies.signed[:session_token])
107
- Current.session = session
108
- else
109
- redirect_to sign_in_path
110
- end
111
- end
112
-
113
- def set_current_request_details
114
- Current.user_agent = request.user_agent
115
- Current.ip_address = request.ip
116
- end
117
- CODE
118
-
119
- inject_code = options.api? ? api_code : html_code
120
- inject_into_class "app/controllers/application_controller.rb", "ApplicationController", optimize_indentation(inject_code, 2), verbose: false
121
- end
122
-
123
66
  def create_controllers
67
+ template "controllers/#{format_folder}/application_controller.rb", "app/controllers/application_controller.rb", force: true
68
+
124
69
  directory "controllers/#{format_folder}/identity", "app/controllers/identity"
125
70
  template "controllers/#{format_folder}/passwords_controller.rb", "app/controllers/passwords_controller.rb"
126
71
  template "controllers/#{format_folder}/registrations_controller.rb", "app/controllers/registrations_controller.rb"
@@ -153,29 +98,31 @@ class AuthenticationGenerator < Rails::Generators::NamedBase
153
98
  def add_routes
154
99
  if omniauthable?
155
100
  route "post '/auth/:provider/callback', to: 'sessions/omniauth#create'"
156
- route "get '/auth/:provider/callback', to: 'sessions/omniauth#create'"
157
- route "get '/auth/failure', to: 'sessions/omniauth#failure'"
101
+ route "get '/auth/:provider/callback', to: 'sessions/omniauth#create'"
102
+ route "get '/auth/failure', to: 'sessions/omniauth#failure'"
158
103
  end
159
104
 
160
105
  if options.trackable?
161
106
  route "resources :events, only: :index", namespace: :authentications
162
107
  end
163
108
 
164
- route "resource :password_reset, only: [:new, :edit, :create, :update]", namespace: :identity
109
+ route "resource :password_reset, only: [:new, :edit, :create, :update]", namespace: :identity
165
110
  route "resource :email_verification, only: [:edit, :create]", namespace: :identity
166
- route "resource :email, only: [:edit, :update]", namespace: :identity
111
+ route "resource :email, only: [:edit, :update]", namespace: :identity
167
112
  route "resource :sudo, only: [:new, :create]", namespace: :sessions
113
+ route "resource :password, only: [:edit, :update]"
168
114
  route "resources :sessions, only: [:index, :show, :destroy]"
169
- route "resource :password, only: [:edit, :update]"
170
115
  route "post 'sign_up', to: 'registrations#create'"
171
- route "get 'sign_up', to: 'registrations#new'" unless options.api?
116
+ route "get 'sign_up', to: 'registrations#new'" unless options.api?
172
117
  route "post 'sign_in', to: 'sessions#create'"
173
- route "get 'sign_in', to: 'sessions#new'" unless options.api?
118
+ route "get 'sign_in', to: 'sessions#new'" unless options.api?
174
119
  end
175
120
 
176
121
  def create_test_files
177
122
  directory "test_unit/controllers/#{format_folder}", "test/controllers"
178
123
  directory "test_unit/system", "test/system" unless options.api?
124
+ template "test_unit/test_helper.rb", "test/test_helper.rb", force: true
125
+ template "test_unit/application_system_test_case.rb", "test/application_system_test_case.rb", force: true unless options.api?
179
126
  end
180
127
 
181
128
  private
@@ -0,0 +1,26 @@
1
+ class ApplicationController < ActionController::API
2
+ include ActionController::HttpAuthentication::Token::ControllerMethods
3
+
4
+ before_action :set_current_request_details
5
+ before_action :authenticate
6
+
7
+ def require_sudo
8
+ if Current.session.sudo_at < 30.minutes.ago
9
+ render json: { error: "Enter your password to continue" }, status: :forbidden
10
+ end
11
+ end
12
+
13
+ private
14
+ def authenticate
15
+ if session = authenticate_with_http_token { |token, _| Session.find_signed(token) }
16
+ Current.session = session
17
+ else
18
+ request_http_token_authentication
19
+ end
20
+ end
21
+
22
+ def set_current_request_details
23
+ Current.user_agent = request.user_agent
24
+ Current.ip_address = request.ip
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ class ApplicationController < ActionController::Base
2
+ before_action :set_current_request_details
3
+ before_action :authenticate
4
+
5
+ def require_sudo
6
+ if Current.session.sudo_at < 30.minutes.ago
7
+ redirect_to new_sessions_sudo_path(proceed_to_url: request.url)
8
+ end
9
+ end
10
+
11
+ private
12
+ def authenticate
13
+ if session = Session.find_by_id(cookies.signed[:session_token])
14
+ Current.session = session
15
+ else
16
+ redirect_to sign_in_path
17
+ end
18
+ end
19
+
20
+ def set_current_request_details
21
+ Current.user_agent = request.user_agent
22
+ Current.ip_address = request.ip
23
+ end
24
+ end
@@ -9,8 +9,8 @@ class <%= class_name %> < ApplicationRecord
9
9
  validates :email, presence: true, uniqueness: true
10
10
  validates_format_of :email, with: /\A[^@\s]+@[^@\s]+\z/
11
11
 
12
- validates_length_of :password, minimum: 12, allow_blank: true
13
- validates_format_of :password, with: /(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])/, allow_blank: true, message: "might easily be guessed"
12
+ validates_length_of :password, minimum: 12, allow_nil: true
13
+ validates_format_of :password, with: /(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])/, allow_nil: true, message: "might easily be guessed"
14
14
  <% if options.pwned? -%>
15
15
  validates :password, not_pwned: { message: "might easily be guessed" }
16
16
  <% end -%>
@@ -0,0 +1,15 @@
1
+ require "test_helper"
2
+
3
+ class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
4
+ driven_by :selenium, using: :chrome, screen_size: [1400, 1400]
5
+
6
+ def sign_in_as(<%= singular_table_name %>)
7
+ visit sign_in_url
8
+ fill_in :email, with: <%= singular_table_name %>.email
9
+ fill_in :password, with: "Secret1*3*5*"
10
+ click_on "Sign in"
11
+
12
+ assert_current_path root_url
13
+ return <%= singular_table_name %>
14
+ end
15
+ end
@@ -37,8 +37,4 @@ class Identity::EmailVerificationsControllerTest < ActionDispatch::IntegrationTe
37
37
  assert_response :bad_request
38
38
  assert_equal "That email verification link is invalid", response.parsed_body["error"]
39
39
  end
40
-
41
- def sign_in_as(<%= singular_table_name %>)
42
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
43
- end
44
40
  end
@@ -18,8 +18,4 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
18
18
  assert_response :forbidden
19
19
  assert_equal "Enter your password to continue", response.parsed_body["error"]
20
20
  end
21
-
22
- def sign_in_as(<%= singular_table_name %>)
23
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
24
- end
25
21
  end
@@ -16,8 +16,4 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
16
16
  assert_response :bad_request
17
17
  assert_equal "The current password you entered is incorrect", response.parsed_body["error"]
18
18
  end
19
-
20
- def sign_in_as(<%= singular_table_name %>)
21
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
22
- end
23
19
  end
@@ -17,8 +17,4 @@ class Sessions::SudosControllerTest < ActionDispatch::IntegrationTest
17
17
  assert_response :bad_request
18
18
  assert_equal "The password you entered is incorrect", response.parsed_body["error"]
19
19
  end
20
-
21
- def sign_in_as(<%= singular_table_name %>)
22
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
23
- end
24
20
  end
@@ -31,8 +31,4 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
31
31
  delete session_url(@<%= singular_table_name %>.sessions.last), headers: { "Authorization" => "Bearer #{@token}" }
32
32
  assert_response :no_content
33
33
  end
34
-
35
- def sign_in_as(<%= singular_table_name %>)
36
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
37
- end
38
34
  end
@@ -37,8 +37,4 @@ class Identity::EmailVerificationsControllerTest < ActionDispatch::IntegrationTe
37
37
  assert_redirected_to edit_identity_email_url
38
38
  assert_equal "That email verification link is invalid", flash[:alert]
39
39
  end
40
-
41
- def sign_in_as(<%= singular_table_name %>)
42
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
43
- end
44
40
  end
@@ -28,8 +28,4 @@ class Identity::EmailsControllerTest < ActionDispatch::IntegrationTest
28
28
  patch identity_email_url, params: { email: "new_email@hey.com" }
29
29
  assert_redirected_to new_sessions_sudo_url(proceed_to_url: identity_email_url)
30
30
  end
31
-
32
- def sign_in_as(<%= singular_table_name %>)
33
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
34
- end
35
31
  end
@@ -21,8 +21,4 @@ class PasswordsControllerTest < ActionDispatch::IntegrationTest
21
21
  assert_redirected_to edit_password_url
22
22
  assert_equal "The current password you entered is incorrect", flash[:alert]
23
23
  end
24
-
25
- def sign_in_as(<%= singular_table_name %>)
26
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
27
- end
28
24
  end
@@ -19,8 +19,4 @@ class Sessions::SudosControllerTest < ActionDispatch::IntegrationTest
19
19
  post sessions_sudo_url, params: { password: "SecretWrong1*3", proceed_to_url: edit_password_url }
20
20
  assert_redirected_to new_sessions_sudo_url(proceed_to_url: edit_password_url)
21
21
  end
22
-
23
- def sign_in_as(<%= singular_table_name %>)
24
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
25
- end
26
22
  end
@@ -45,8 +45,4 @@ class SessionsControllerTest < ActionDispatch::IntegrationTest
45
45
  follow_redirect!
46
46
  assert_redirected_to sign_in_url
47
47
  end
48
-
49
- def sign_in_as(<%= singular_table_name %>)
50
- post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
51
- end
52
48
  end
@@ -22,14 +22,4 @@ class Identity::EmailsTest < ApplicationSystemTestCase
22
22
 
23
23
  assert_text "We sent a verification email to your email address"
24
24
  end
25
-
26
- def sign_in_as(<%= singular_table_name %>)
27
- visit sign_in_url
28
- fill_in :email, with: <%= singular_table_name %>.email
29
- fill_in :password, with: "Secret1*3*5*"
30
- click_on "Sign in"
31
-
32
- assert_current_path root_url
33
- return <%= singular_table_name %>
34
- end
35
25
  end
@@ -15,14 +15,4 @@ class PasswordsTest < ApplicationSystemTestCase
15
15
 
16
16
  assert_text "Your password has been changed"
17
17
  end
18
-
19
- def sign_in_as(<%= singular_table_name %>)
20
- visit sign_in_url
21
- fill_in :email, with: <%= singular_table_name %>.email
22
- fill_in :password, with: "Secret1*3*5*"
23
- click_on "Sign in"
24
-
25
- assert_current_path root_url
26
- return <%= singular_table_name %>
27
- end
28
18
  end
@@ -12,14 +12,4 @@ class Sessions::SudosTest < ApplicationSystemTestCase
12
12
 
13
13
  assert_selector "h1", text: "Change your password"
14
14
  end
15
-
16
- def sign_in_as(<%= singular_table_name %>)
17
- visit sign_in_url
18
- fill_in :email, with: <%= singular_table_name %>.email
19
- fill_in :password, with: "Secret1*3*5*"
20
- click_on "Sign in"
21
-
22
- assert_current_path root_url
23
- return <%= singular_table_name %>
24
- end
25
15
  end
@@ -27,14 +27,4 @@ class SessionsTest < ApplicationSystemTestCase
27
27
  click_on "Log out"
28
28
  assert_text "That session has been logged out"
29
29
  end
30
-
31
- def sign_in_as(<%= singular_table_name %>)
32
- visit sign_in_url
33
- fill_in :email, with: <%= singular_table_name %>.email
34
- fill_in :password, with: "Secret1*3*5*"
35
- click_on "Sign in"
36
-
37
- assert_current_path root_url
38
- return <%= singular_table_name %>
39
- end
40
30
  end
@@ -0,0 +1,22 @@
1
+ ENV["RAILS_ENV"] ||= "test"
2
+ require_relative "../config/environment"
3
+ require "rails/test_help"
4
+
5
+ class ActiveSupport::TestCase
6
+ # Run tests in parallel with specified workers
7
+ parallelize(workers: :number_of_processors)
8
+
9
+ # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
10
+ fixtures :all
11
+
12
+ # Add more helper methods to be used by all tests here...
13
+ <% if options.api? -%>
14
+ def sign_in_as(<%= singular_table_name %>)
15
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); [<%= singular_table_name %>, response.headers["X-Session-Token"]]
16
+ end
17
+ <% else -%>
18
+ def sign_in_as(<%= singular_table_name %>)
19
+ post(sign_in_url, params: { email: <%= singular_table_name %>.email, password: "Secret1*3*5*" }); <%= singular_table_name %>
20
+ end
21
+ <% end -%>
22
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: authentication-zero
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.9.2
4
+ version: 2.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nixon
@@ -35,6 +35,7 @@ files:
35
35
  - lib/generators/authentication/authentication_generator.rb
36
36
  - lib/generators/authentication/templates/config/initializers/omniauth.rb
37
37
  - lib/generators/authentication/templates/config/redis/shared.yml
38
+ - lib/generators/authentication/templates/controllers/api/application_controller.rb.tt
38
39
  - lib/generators/authentication/templates/controllers/api/authentications/events_controller.rb.tt
39
40
  - lib/generators/authentication/templates/controllers/api/identity/email_verifications_controller.rb.tt
40
41
  - lib/generators/authentication/templates/controllers/api/identity/emails_controller.rb.tt
@@ -43,6 +44,7 @@ files:
43
44
  - lib/generators/authentication/templates/controllers/api/registrations_controller.rb.tt
44
45
  - lib/generators/authentication/templates/controllers/api/sessions/sudos_controller.rb.tt
45
46
  - lib/generators/authentication/templates/controllers/api/sessions_controller.rb.tt
47
+ - lib/generators/authentication/templates/controllers/html/application_controller.rb.tt
46
48
  - lib/generators/authentication/templates/controllers/html/authentications/events_controller.rb.tt
47
49
  - lib/generators/authentication/templates/controllers/html/identity/email_verifications_controller.rb.tt
48
50
  - lib/generators/authentication/templates/controllers/html/identity/emails_controller.rb.tt
@@ -77,6 +79,7 @@ files:
77
79
  - lib/generators/authentication/templates/models/locking.rb.tt
78
80
  - lib/generators/authentication/templates/models/model.rb.tt
79
81
  - lib/generators/authentication/templates/models/session.rb.tt
82
+ - lib/generators/authentication/templates/test_unit/application_system_test_case.rb.tt
80
83
  - lib/generators/authentication/templates/test_unit/controllers/api/identity/email_verifications_controller_test.rb.tt
81
84
  - lib/generators/authentication/templates/test_unit/controllers/api/identity/emails_controller_test.rb.tt
82
85
  - lib/generators/authentication/templates/test_unit/controllers/api/identity/password_resets_controller_test.rb.tt
@@ -98,6 +101,7 @@ files:
98
101
  - lib/generators/authentication/templates/test_unit/system/registrations_test.rb.tt
99
102
  - lib/generators/authentication/templates/test_unit/system/sessions/sudos_test.rb.tt
100
103
  - lib/generators/authentication/templates/test_unit/system/sessions_test.rb.tt
104
+ - lib/generators/authentication/templates/test_unit/test_helper.rb.tt
101
105
  homepage: https://github.com/lazaronixon/authentication-zero
102
106
  licenses:
103
107
  - MIT