morpho 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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>