morpho 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/app/api/concerns/morpho/grape/http_responses.rb +41 -0
  4. data/app/api/concerns/morpho/grape/jwt_authentication.rb +77 -0
  5. data/app/api/concerns/morpho/grape/user_activation.rb +13 -0
  6. data/app/api/concerns/morpho/grape/user_password_reset.rb +13 -0
  7. data/app/api/concerns/morpho/grape/user_registration.rb +19 -0
  8. data/app/api/concerns/morpho/grape/user_unlock.rb +13 -0
  9. data/app/api/morpho/api.rb +24 -0
  10. data/app/api/morpho/entities/authentication_token.rb +8 -0
  11. data/app/api/morpho/entities/user.rb +7 -0
  12. data/app/api/morpho/entities/user_sign_in.rb +8 -0
  13. data/app/api/morpho/entities/user_sign_up.rb +9 -0
  14. data/app/api/morpho/resources/activations.rb +29 -0
  15. data/app/api/morpho/resources/passwords.rb +25 -0
  16. data/app/api/morpho/resources/tokens.rb +19 -0
  17. data/app/api/morpho/resources/unlocks.rb +29 -0
  18. data/app/api/morpho/resources/users.rb +19 -0
  19. data/app/assets/images/morpho/morpho.png +0 -0
  20. data/app/assets/images/morpho/morpho.svg +89 -0
  21. data/app/assets/stylesheets/morpho/application.css +74 -1
  22. data/app/controllers/morpho/activations_controller.rb +44 -0
  23. data/app/controllers/morpho/application_controller.rb +9 -0
  24. data/app/controllers/morpho/home_controller.rb +6 -0
  25. data/app/controllers/morpho/passwords_controller.rb +56 -0
  26. data/app/controllers/morpho/sessions_controller.rb +24 -0
  27. data/app/controllers/morpho/unlocks_controller.rb +44 -0
  28. data/app/controllers/morpho/users_controller.rb +25 -0
  29. data/app/mailers/morpho/application_mailer.rb +0 -1
  30. data/app/mailers/morpho/user_mailer.rb +31 -0
  31. data/app/models/morpho/authentication.rb +5 -0
  32. data/app/models/morpho/user.rb +60 -0
  33. data/app/views/layouts/morpho/application.html.erb +6 -4
  34. data/app/views/layouts/morpho/mailer.html.erb +13 -0
  35. data/app/views/layouts/morpho/mailer.text.erb +1 -0
  36. data/app/views/morpho/activations/new.html.erb +16 -0
  37. data/app/views/morpho/home/index.html.erb +4 -0
  38. data/app/views/morpho/passwords/edit.html.erb +18 -0
  39. data/app/views/morpho/passwords/new.html.erb +16 -0
  40. data/app/views/morpho/sessions/new.html.erb +23 -0
  41. data/app/views/morpho/unlocks/new.html.erb +16 -0
  42. data/app/views/morpho/user_mailer/activation_needed_email.html.erb +7 -0
  43. data/app/views/morpho/user_mailer/activation_needed_email.text.erb +7 -0
  44. data/app/views/morpho/user_mailer/activation_success_email.html.erb +7 -0
  45. data/app/views/morpho/user_mailer/activation_success_email.text.erb +7 -0
  46. data/app/views/morpho/user_mailer/reset_password_email.html.erb +7 -0
  47. data/app/views/morpho/user_mailer/reset_password_email.text.erb +7 -0
  48. data/app/views/morpho/user_mailer/unlock_token_email.html.erb +7 -0
  49. data/app/views/morpho/user_mailer/unlock_token_email.text.erb +7 -0
  50. data/app/views/morpho/users/new.html.erb +20 -0
  51. data/config/initializers/flash_rails_messages_skeleton.rb +22 -0
  52. data/config/initializers/simple_form.rb +182 -0
  53. data/config/initializers/sorcery.rb +513 -0
  54. data/config/locales/morpho.en.yml +93 -0
  55. data/config/routes.rb +25 -0
  56. data/db/migrate/20180919162009_sorcery_core.rb +13 -0
  57. data/db/migrate/20180919162055_sorcery_remember_me.rb +8 -0
  58. data/db/migrate/20180919162056_sorcery_reset_password.rb +10 -0
  59. data/db/migrate/20180919162057_sorcery_user_activation.rb +9 -0
  60. data/db/migrate/20180919162058_sorcery_brute_force_protection.rb +9 -0
  61. data/db/migrate/20180919162059_sorcery_activity_logging.rb +10 -0
  62. data/db/migrate/20180919162100_sorcery_external.rb +12 -0
  63. data/lib/generators/morpho/install/install_generator.rb +7 -0
  64. data/lib/generators/morpho/install/templates/config/initializers/morpho.rb +17 -0
  65. data/lib/generators/morpho/install/templates/public/favicon-16x16.png +0 -0
  66. data/lib/generators/morpho/install/templates/public/favicon-32x32.png +0 -0
  67. data/lib/generators/morpho/install/templates/public/favicon.ico +0 -0
  68. data/lib/morpho.rb +15 -2
  69. data/lib/morpho/configuration.rb +24 -0
  70. data/lib/morpho/configurations/api.rb +31 -0
  71. data/lib/morpho/configurations/auth.rb +11 -0
  72. data/lib/morpho/configurations/jwt.rb +17 -0
  73. data/lib/morpho/configurations/mailer.rb +23 -0
  74. data/lib/morpho/engine.rb +33 -0
  75. data/lib/morpho/loader.rb +11 -0
  76. data/lib/morpho/version.rb +1 -1
  77. data/lib/tasks/morpho_tasks.rake +1 -1
  78. data/lib/templates/erb/scaffold/_form.html.erb +15 -0
  79. metadata +223 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7e959069f39a025dbaf68b5940a67e344b0ea493670520c9d5ee5c5ab4245e5a
4
- data.tar.gz: ddcb89b4d6857e36d2faf8def8989a19f74ed5fd8814d5c07ca2dbd096b767cf
3
+ metadata.gz: e508e3ad112945f755ed280843449493c214243896d9cb526b6d65fc6803c01c
4
+ data.tar.gz: 963b8391b3aa1e5955b1743dfa5b412618aa25ba24099264db011beaf3788a2b
5
5
  SHA512:
6
- metadata.gz: d35f7d3fb1a97069c940e1a163f2f235b064c7c900965be3ac053173505dd31c7863f61c299356e20800c59a856f3266d3fe812b04edd741c4d77d3bb05e1bb9
7
- data.tar.gz: 699ef6c4358c4785d9792971f3dd9d75186f06613f4260aed8b7b384eb6e69b84a6e858892565e4ff9da2bddb24074415b5a560ed2de650daed13ed6a6436447
6
+ metadata.gz: df72bb86c829de2c8f18b419cbe38cbf60a47d39b3cb6c54a97000996708a8c7f20ba2d32ad3d632ca7e57a350cd830bf13e93b8958418a33c9cf7f502c34834
7
+ data.tar.gz: 50de12851535679fab3886e8cec10ee48f68a65b66fb057ecc3b445b880c6c982e48d908d91e96792285bbd6435f281cf4a9b2f548761fbbdace60ed083fe617
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
14
14
  rdoc.rdoc_files.include('lib/**/*.rb')
15
15
  end
16
16
 
17
- APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
17
+ APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
18
18
  load 'rails/tasks/engine.rake'
19
19
 
20
20
  load 'rails/tasks/statistics.rake'
@@ -0,0 +1,41 @@
1
+ module Morpho
2
+ module Grape
3
+ module HTTPResponses
4
+ extend ActiveSupport::Concern
5
+
6
+ protected
7
+
8
+ def render_bad_request
9
+ error!({ message: I18n.t('morpho.api.messages.bad_request') }, 400)
10
+ end
11
+
12
+ def render_unauthorized
13
+ error!({ message: I18n.t('morpho.api.messages.unauthorized') }, 401)
14
+ end
15
+
16
+ def render_payment_required
17
+ error!({ message: I18n.t('morpho.api.messages.payment_required') }, 402)
18
+ end
19
+
20
+ def render_forbidden
21
+ error!({ message: I18n.t('morpho.api.messages.forbidden') }, 403)
22
+ end
23
+
24
+ def render_not_found
25
+ error!({ message: I18n.t('morpho.api.messages.not_found') }, 404)
26
+ end
27
+
28
+ def render_method_not_allowed
29
+ error!({ message: I18n.t('morpho.api.messages.method_not_allowed') }, 405)
30
+ end
31
+
32
+ def render_unprocessable_entity(errors)
33
+ error!({ message: I18n.t('morpho.api.messages.unprocessable_entity'), errors: errors }, 422)
34
+ end
35
+
36
+ def render_no_content
37
+ body false
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,77 @@
1
+ module Morpho
2
+ module Grape
3
+ module JWTAuthentication
4
+ extend ActiveSupport::Concern
5
+
6
+ protected
7
+
8
+ def logged_in?
9
+ !!current_user
10
+ end
11
+
12
+ def require_login
13
+ render_unauthorized unless logged_in?
14
+ end
15
+
16
+ def login(user_params)
17
+ user = User.find_by(email: user_params[:email])
18
+
19
+ if user
20
+ if user.active?
21
+ if !user.login_locked?
22
+ if user.valid_password?(user_params[:password])
23
+ token = user_payload(user)
24
+
25
+ present token, with: Morpho::Entities::AuthenticationToken
26
+ else
27
+ user.register_failed_login!
28
+ render_unauthorized
29
+ end
30
+ else
31
+ render_unauthorized
32
+ end
33
+ else
34
+ render_unauthorized
35
+ end
36
+ else
37
+ render_unauthorized
38
+ end
39
+ end
40
+
41
+ def current_user
42
+ @current_user ||= Morpho::User.find_by(email: jwt_token[:email])
43
+ rescue
44
+ nil
45
+ end
46
+
47
+ def http_token
48
+ @http_token ||= if request.headers[Morpho.config.jwt.header].present?
49
+ request.headers[Morpho.config.jwt.header].split(' ').last
50
+ end
51
+ end
52
+
53
+ def jwt_token
54
+ @jwt_token ||= HashWithIndifferentAccess.new(jwt_decode(http_token).first)
55
+ end
56
+
57
+ def jwt_encode(payload)
58
+ JWT.encode(payload, Morpho.config.jwt.secret, Morpho.config.jwt.algorithm)
59
+ end
60
+
61
+ def jwt_decode(token)
62
+ begin
63
+ return JWT.decode(token, Morpho.config.jwt.secret, true, { algorithm: Morpho.config.jwt.algorithm })
64
+ rescue
65
+ nil
66
+ end
67
+ end
68
+
69
+ def user_payload(user)
70
+ expires_at = Time.now.to_i + Morpho.config.jwt.expiration_time
71
+ issued_at = Time.now.to_i
72
+
73
+ { token: jwt_encode({ exp: expires_at, iat: issued_at, email: user.email }), expires_at: expires_at }
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,13 @@
1
+ module Morpho
2
+ module Grape
3
+ module UserActivation
4
+ extend ActiveSupport::Concern
5
+
6
+ protected
7
+
8
+ def current_user
9
+ @current_user ||= Morpho::User.find_by(params[:user])
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Morpho
2
+ module Grape
3
+ module UserPasswordReset
4
+ extend ActiveSupport::Concern
5
+
6
+ protected
7
+
8
+ def current_user
9
+ @current_user ||= Morpho::User.find_by(params[:user])
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,19 @@
1
+ module Morpho
2
+ module Grape
3
+ module UserRegistration
4
+ extend ActiveSupport::Concern
5
+
6
+ protected
7
+
8
+ def register(user_params)
9
+ user = Morpho::User.new(user_params)
10
+
11
+ if user.save
12
+ present user, with: Morpho::Entities::User
13
+ else
14
+ render_unprocessable_entity(user.errors)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module Morpho
2
+ module Grape
3
+ module UserUnlock
4
+ extend ActiveSupport::Concern
5
+
6
+ protected
7
+
8
+ def current_user
9
+ @current_user ||= Morpho::User.find_by(params[:user])
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ module Morpho
2
+ class API < ::Grape::API
3
+ format :json
4
+ rescue_from :all
5
+
6
+ mount Morpho::Resources::Users
7
+ mount Morpho::Resources::Tokens
8
+ mount Morpho::Resources::Passwords
9
+ mount Morpho::Resources::Unlocks
10
+ mount Morpho::Resources::Activations
11
+
12
+ add_swagger_documentation({
13
+ info: {
14
+ title: Morpho.config.api.title,
15
+ description: Morpho.config.api.description,
16
+ models: Morpho.config.api.entities
17
+ }
18
+ })
19
+
20
+ route :any, '*path' do
21
+ error!({ message: I18n.t('morpho.api.messages.not_found') }, 404)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ module Morpho
2
+ module Entities
3
+ class AuthenticationToken < ::Grape::Entity
4
+ expose :token, documentation: { type: 'string', desc: 'User authentication token', required: true }
5
+ expose :expires_at, documentation: { type: 'string', desc: 'Authentication token expiration date in millis', required: true }
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module Morpho
2
+ module Entities
3
+ class User < ::Grape::Entity
4
+ expose :email, documentation: { type: 'string', desc: 'User email address' }
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ module Morpho
2
+ module Entities
3
+ class UserSignIn < ::Grape::Entity
4
+ expose :email, documentation: { type: 'string', desc: 'User email address' }
5
+ expose :password, documentation: { type: 'string', desc: 'User password' }
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module Morpho
2
+ module Entities
3
+ class UserSignUp < ::Grape::Entity
4
+ expose :email, documentation: { type: 'string', desc: 'User email address' }
5
+ expose :password, documentation: { type: 'string', desc: 'User password' }
6
+ expose :password_confirmation, documentation: { type: 'string', desc: 'User password confirmation' }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ module Morpho
2
+ module Resources
3
+ class Activations < ::Grape::API
4
+ helpers Morpho::Grape::HTTPResponses, Morpho::Grape::UserActivation
5
+
6
+ namespace :activations do
7
+ desc 'Request user activation token' do
8
+ success Morpho::Entities::User
9
+ end
10
+ params do
11
+ requires :user, type: Morpho::Entities::User
12
+ end
13
+ post do
14
+ if current_user
15
+ if !current_user.active?
16
+ current_user.resend_activation_needed_email!
17
+
18
+ present current_user, with: Morpho::Entities::User
19
+ else
20
+ render_method_not_allowed
21
+ end
22
+ else
23
+ render_not_found
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,25 @@
1
+ module Morpho
2
+ module Resources
3
+ class Passwords < ::Grape::API
4
+ helpers Morpho::Grape::HTTPResponses, Morpho::Grape::UserPasswordReset
5
+
6
+ namespace :passwords do
7
+ desc 'Request user reset password token' do
8
+ success Morpho::Entities::User
9
+ end
10
+ params do
11
+ requires :user, type: Morpho::Entities::User
12
+ end
13
+ post do
14
+ if current_user
15
+ current_user.deliver_reset_password_instructions!
16
+
17
+ present current_user, with: Morpho::Entities::User
18
+ else
19
+ render_not_found
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ module Morpho
2
+ module Resources
3
+ class Tokens < ::Grape::API
4
+ helpers Morpho::Grape::HTTPResponses, Morpho::Grape::JWTAuthentication
5
+
6
+ namespace :tokens do
7
+ desc 'Request user authentication token' do
8
+ success Morpho::Entities::AuthenticationToken
9
+ end
10
+ params do
11
+ requires :user, type: Morpho::Entities::UserSignIn
12
+ end
13
+ post do
14
+ login(params[:user])
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ module Morpho
2
+ module Resources
3
+ class Unlocks < ::Grape::API
4
+ helpers Morpho::Grape::HTTPResponses, Morpho::Grape::UserUnlock
5
+
6
+ namespace :unlocks do
7
+ desc 'Request user unlock token' do
8
+ success Morpho::Entities::User
9
+ end
10
+ params do
11
+ requires :user, type: Morpho::Entities::User
12
+ end
13
+ post do
14
+ if current_user
15
+ if current_user.login_locked?
16
+ current_user.resend_unlock_token_email!
17
+
18
+ present current_user, with: Morpho::Entities::User
19
+ else
20
+ render_method_not_allowed
21
+ end
22
+ else
23
+ render_not_found
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ module Morpho
2
+ module Resources
3
+ class Users < ::Grape::API
4
+ helpers Morpho::Grape::HTTPResponses, Morpho::Grape::UserRegistration
5
+
6
+ namespace :users do
7
+ desc 'User registration' do
8
+ success Morpho::Entities::User
9
+ end
10
+ params do
11
+ requires :user, type: Morpho::Entities::UserSignUp
12
+ end
13
+ post do
14
+ register(params[:user])
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,89 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5
+ width="35.616px" height="35.616px" viewBox="0 0 35.616 35.616" style="enable-background:new 0 0 35.616 35.616;"
6
+ xml:space="preserve">
7
+ <g>
8
+ <g>
9
+ <polygon points="7.526,6.141 7.526,11.054 11.78,8.598 "/>
10
+ <polygon points="7.526,12.282 7.526,17.193 11.78,14.738 "/>
11
+ <polygon points="7.526,24.562 7.526,29.475 11.78,27.02 "/>
12
+ <polygon points="7.881,30.09 12.135,32.546 12.135,27.635 "/>
13
+ <polygon points="7.881,17.809 12.135,20.266 12.135,15.352 "/>
14
+ <polygon points="7.881,11.668 12.135,14.124 12.135,9.211 "/>
15
+ <polygon points="7.881,5.527 12.135,7.983 12.135,3.071 "/>
16
+ <polygon points="12.844,3.071 12.844,7.983 17.099,5.527 "/>
17
+ <polygon points="12.844,9.211 12.844,14.124 17.099,11.668 "/>
18
+ <polygon points="12.844,15.352 12.844,20.266 17.099,17.809 "/>
19
+ <polygon points="12.844,21.493 12.844,26.405 17.099,23.949 "/>
20
+ <polygon points="12.844,27.635 12.844,32.546 17.099,30.09 "/>
21
+ <polygon points="13.199,33.16 17.453,35.616 17.453,30.704 "/>
22
+ <polygon points="13.199,27.02 17.453,29.475 17.453,24.562 "/>
23
+ <polygon points="13.199,20.879 17.453,23.335 17.453,18.422 "/>
24
+ <polygon points="13.199,14.738 17.453,17.193 17.453,12.282 "/>
25
+ <polygon points="13.199,2.456 17.453,4.913 17.453,0 "/>
26
+ <polygon points="2.208,9.211 2.208,14.124 6.462,11.668 "/>
27
+ <polygon points="2.208,15.352 2.208,20.266 6.462,17.809 "/>
28
+ <polygon points="2.208,21.493 2.208,26.405 6.462,23.949 "/>
29
+ <polygon points="2.562,27.02 6.817,29.475 6.817,24.562 "/>
30
+ <polygon points="2.562,20.879 6.817,23.335 6.817,18.422 "/>
31
+ <polygon points="2.562,14.738 6.817,17.193 6.817,12.282 "/>
32
+ <polygon points="2.562,8.598 6.817,11.054 6.817,6.141 "/>
33
+ <polygon points="18.162,0 18.162,4.913 22.416,2.456 "/>
34
+ <polygon points="18.162,12.282 18.162,17.193 22.416,14.738 "/>
35
+ <polygon points="18.162,18.422 18.162,23.335 22.416,20.879 "/>
36
+ <polygon points="18.162,24.562 18.162,29.475 22.416,27.02 "/>
37
+ <polygon points="18.162,30.704 18.162,35.616 22.416,33.16 "/>
38
+ <polygon points="18.517,30.09 22.771,32.546 22.771,27.635 "/>
39
+ <polygon points="18.517,23.949 22.771,26.405 22.771,21.493 "/>
40
+ <polygon points="18.517,17.809 22.771,20.266 22.771,15.352 "/>
41
+ <polygon points="18.517,11.668 22.771,14.124 22.771,9.211 "/>
42
+ <polygon points="18.517,5.527 22.771,7.983 22.771,3.071 "/>
43
+ <polygon points="23.479,3.071 23.479,7.983 27.735,5.527 "/>
44
+ <polygon points="23.479,9.211 23.479,14.124 27.735,11.668 "/>
45
+ <polygon points="23.479,15.352 23.479,20.266 27.735,17.809 "/>
46
+ <polygon points="23.479,27.635 23.479,32.546 27.735,30.09 "/>
47
+ <polygon points="23.835,27.02 28.089,29.475 28.089,24.562 "/>
48
+ <polygon points="23.835,14.738 28.089,17.193 28.089,12.282 "/>
49
+ <polygon points="23.835,8.598 28.089,11.054 28.089,6.141 "/>
50
+ <polygon points="28.798,6.141 28.798,11.054 33.052,8.598 "/>
51
+ <polygon points="28.798,12.282 28.798,17.193 33.052,14.738 "/>
52
+ <polygon points="28.798,18.422 28.798,23.335 33.052,20.879 "/>
53
+ <polygon points="28.798,24.562 28.798,29.475 33.052,27.02 "/>
54
+ <polygon points="29.153,23.949 33.408,26.405 33.408,21.493 "/>
55
+ <polygon points="29.153,17.809 33.408,20.266 33.408,15.352 "/>
56
+ <polygon points="29.153,11.668 33.408,14.124 33.408,9.211 "/>
57
+ </g>
58
+ </g>
59
+ <g>
60
+ </g>
61
+ <g>
62
+ </g>
63
+ <g>
64
+ </g>
65
+ <g>
66
+ </g>
67
+ <g>
68
+ </g>
69
+ <g>
70
+ </g>
71
+ <g>
72
+ </g>
73
+ <g>
74
+ </g>
75
+ <g>
76
+ </g>
77
+ <g>
78
+ </g>
79
+ <g>
80
+ </g>
81
+ <g>
82
+ </g>
83
+ <g>
84
+ </g>
85
+ <g>
86
+ </g>
87
+ <g>
88
+ </g>
89
+ </svg>