morpho 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/app/api/concerns/morpho/grape/http_responses.rb +41 -0
- data/app/api/concerns/morpho/grape/jwt_authentication.rb +77 -0
- data/app/api/concerns/morpho/grape/user_activation.rb +13 -0
- data/app/api/concerns/morpho/grape/user_password_reset.rb +13 -0
- data/app/api/concerns/morpho/grape/user_registration.rb +19 -0
- data/app/api/concerns/morpho/grape/user_unlock.rb +13 -0
- data/app/api/morpho/api.rb +24 -0
- data/app/api/morpho/entities/authentication_token.rb +8 -0
- data/app/api/morpho/entities/user.rb +7 -0
- data/app/api/morpho/entities/user_sign_in.rb +8 -0
- data/app/api/morpho/entities/user_sign_up.rb +9 -0
- data/app/api/morpho/resources/activations.rb +29 -0
- data/app/api/morpho/resources/passwords.rb +25 -0
- data/app/api/morpho/resources/tokens.rb +19 -0
- data/app/api/morpho/resources/unlocks.rb +29 -0
- data/app/api/morpho/resources/users.rb +19 -0
- data/app/assets/images/morpho/morpho.png +0 -0
- data/app/assets/images/morpho/morpho.svg +89 -0
- data/app/assets/stylesheets/morpho/application.css +74 -1
- data/app/controllers/morpho/activations_controller.rb +44 -0
- data/app/controllers/morpho/application_controller.rb +9 -0
- data/app/controllers/morpho/home_controller.rb +6 -0
- data/app/controllers/morpho/passwords_controller.rb +56 -0
- data/app/controllers/morpho/sessions_controller.rb +24 -0
- data/app/controllers/morpho/unlocks_controller.rb +44 -0
- data/app/controllers/morpho/users_controller.rb +25 -0
- data/app/mailers/morpho/application_mailer.rb +0 -1
- data/app/mailers/morpho/user_mailer.rb +31 -0
- data/app/models/morpho/authentication.rb +5 -0
- data/app/models/morpho/user.rb +60 -0
- data/app/views/layouts/morpho/application.html.erb +6 -4
- data/app/views/layouts/morpho/mailer.html.erb +13 -0
- data/app/views/layouts/morpho/mailer.text.erb +1 -0
- data/app/views/morpho/activations/new.html.erb +16 -0
- data/app/views/morpho/home/index.html.erb +4 -0
- data/app/views/morpho/passwords/edit.html.erb +18 -0
- data/app/views/morpho/passwords/new.html.erb +16 -0
- data/app/views/morpho/sessions/new.html.erb +23 -0
- data/app/views/morpho/unlocks/new.html.erb +16 -0
- data/app/views/morpho/user_mailer/activation_needed_email.html.erb +7 -0
- data/app/views/morpho/user_mailer/activation_needed_email.text.erb +7 -0
- data/app/views/morpho/user_mailer/activation_success_email.html.erb +7 -0
- data/app/views/morpho/user_mailer/activation_success_email.text.erb +7 -0
- data/app/views/morpho/user_mailer/reset_password_email.html.erb +7 -0
- data/app/views/morpho/user_mailer/reset_password_email.text.erb +7 -0
- data/app/views/morpho/user_mailer/unlock_token_email.html.erb +7 -0
- data/app/views/morpho/user_mailer/unlock_token_email.text.erb +7 -0
- data/app/views/morpho/users/new.html.erb +20 -0
- data/config/initializers/flash_rails_messages_skeleton.rb +22 -0
- data/config/initializers/simple_form.rb +182 -0
- data/config/initializers/sorcery.rb +513 -0
- data/config/locales/morpho.en.yml +93 -0
- data/config/routes.rb +25 -0
- data/db/migrate/20180919162009_sorcery_core.rb +13 -0
- data/db/migrate/20180919162055_sorcery_remember_me.rb +8 -0
- data/db/migrate/20180919162056_sorcery_reset_password.rb +10 -0
- data/db/migrate/20180919162057_sorcery_user_activation.rb +9 -0
- data/db/migrate/20180919162058_sorcery_brute_force_protection.rb +9 -0
- data/db/migrate/20180919162059_sorcery_activity_logging.rb +10 -0
- data/db/migrate/20180919162100_sorcery_external.rb +12 -0
- data/lib/generators/morpho/install/install_generator.rb +7 -0
- data/lib/generators/morpho/install/templates/config/initializers/morpho.rb +17 -0
- data/lib/generators/morpho/install/templates/public/favicon-16x16.png +0 -0
- data/lib/generators/morpho/install/templates/public/favicon-32x32.png +0 -0
- data/lib/generators/morpho/install/templates/public/favicon.ico +0 -0
- data/lib/morpho.rb +15 -2
- data/lib/morpho/configuration.rb +24 -0
- data/lib/morpho/configurations/api.rb +31 -0
- data/lib/morpho/configurations/auth.rb +11 -0
- data/lib/morpho/configurations/jwt.rb +17 -0
- data/lib/morpho/configurations/mailer.rb +23 -0
- data/lib/morpho/engine.rb +33 -0
- data/lib/morpho/loader.rb +11 -0
- data/lib/morpho/version.rb +1 -1
- data/lib/tasks/morpho_tasks.rake +1 -1
- data/lib/templates/erb/scaffold/_form.html.erb +15 -0
- metadata +223 -2
@@ -10,6 +10,79 @@
|
|
10
10
|
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
11
|
* It is generally better to create a new file per style scope.
|
12
12
|
*
|
13
|
-
*= require_tree .
|
14
13
|
*= require_self
|
15
14
|
*/
|
15
|
+
@import 'https://fonts.googleapis.com/css?family=Raleway:400,300,600';
|
16
|
+
@import 'https://cdn.jsdelivr.net/npm/skeleton-css@2.0.4/css/normalize.css';
|
17
|
+
@import 'https://cdn.jsdelivr.net/npm/skeleton-css@2.0.4/css/skeleton.css';
|
18
|
+
|
19
|
+
body {
|
20
|
+
padding: 2em 1em;
|
21
|
+
}
|
22
|
+
|
23
|
+
.alert {
|
24
|
+
display: block;
|
25
|
+
padding: 1em;
|
26
|
+
border-left: 5px solid;
|
27
|
+
margin-bottom: 1em;
|
28
|
+
}
|
29
|
+
|
30
|
+
.alert-success {
|
31
|
+
background-color: #D5F5E3;
|
32
|
+
border-left-color: #2ECC71;
|
33
|
+
color: #2ECC71;
|
34
|
+
}
|
35
|
+
|
36
|
+
.alert-info {
|
37
|
+
background-color: #D6EAF8;
|
38
|
+
border-left-color: #3498DB;
|
39
|
+
color: #3498DB;
|
40
|
+
}
|
41
|
+
|
42
|
+
.alert-warning {
|
43
|
+
background-color: #FCF3CF;
|
44
|
+
border-left-color: #F1C40F;
|
45
|
+
color: #F1C40F;
|
46
|
+
}
|
47
|
+
|
48
|
+
.alert-error {
|
49
|
+
background-color: #F2D7D5;
|
50
|
+
border-left-color: #C0392B;
|
51
|
+
color: #C0392B;
|
52
|
+
}
|
53
|
+
|
54
|
+
.field_with_errors {
|
55
|
+
padding-bottom: 1em;
|
56
|
+
}
|
57
|
+
|
58
|
+
.field_with_errors input, .field_with_errors textarea, .field_with_errors select {
|
59
|
+
margin-bottom: 0.5em;
|
60
|
+
}
|
61
|
+
|
62
|
+
.field_with_errors span.error {
|
63
|
+
color: #C0392B;
|
64
|
+
text-transform: lowercase;
|
65
|
+
}
|
66
|
+
|
67
|
+
.input label.boolean {
|
68
|
+
display: inline;
|
69
|
+
font-weight: normal;
|
70
|
+
margin-left: 0.5em;
|
71
|
+
}
|
72
|
+
|
73
|
+
ul.unstyled {
|
74
|
+
list-style: none;
|
75
|
+
}
|
76
|
+
|
77
|
+
.sign-in-form, .sign-up-form, .activation-form, .reset-password-form, .unlock-form {
|
78
|
+
position: relative;
|
79
|
+
height: 50vh;
|
80
|
+
}
|
81
|
+
|
82
|
+
.sign-in-form form, .sign-up-form form, .activation-form form, .reset-password-form form, .unlock-form form {
|
83
|
+
position: absolute;
|
84
|
+
top: 50%;
|
85
|
+
left: 50%;
|
86
|
+
transform: translate(-50%, -50%);
|
87
|
+
width: 50vh;
|
88
|
+
}
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Morpho
|
2
|
+
class ActivationsController < ApplicationController
|
3
|
+
skip_before_action :require_login, only: [ :new, :create, :show ]
|
4
|
+
helper_method :user
|
5
|
+
|
6
|
+
def create
|
7
|
+
if user && !user.active?
|
8
|
+
user.resend_activation_needed_email!
|
9
|
+
flash.now[:notice] = I18n.t('morpho.messages.activations.create.success')
|
10
|
+
render :new
|
11
|
+
else
|
12
|
+
flash.now[:error] = I18n.t('morpho.messages.activations.create.failure')
|
13
|
+
render :new
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def show
|
18
|
+
if user && !user.active?
|
19
|
+
user.activate!
|
20
|
+
redirect_to sign_in_path, success: I18n.t('morpho.messages.activations.activate.success')
|
21
|
+
else
|
22
|
+
redirect_to sign_in_path, error: I18n.t('morpho.messages.activations.activate.failure')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def user
|
29
|
+
@user ||= case
|
30
|
+
when %w(show).include?(action_name)
|
31
|
+
Morpho::User.load_from_activation_token(params[:token])
|
32
|
+
when %(create).include?(action_name)
|
33
|
+
Morpho::User.find_by(email_params)
|
34
|
+
else
|
35
|
+
Morpho::User.new
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def email_params
|
41
|
+
params.require(:user).permit(:email)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -1,5 +1,14 @@
|
|
1
1
|
module Morpho
|
2
2
|
class ApplicationController < ActionController::Base
|
3
3
|
protect_from_forgery with: :exception
|
4
|
+
add_flash_types :notice, :alert, :error, :success
|
5
|
+
layout 'morpho/application'
|
6
|
+
before_action :require_login
|
7
|
+
|
8
|
+
protected
|
9
|
+
|
10
|
+
def not_authenticated
|
11
|
+
redirect_to sign_in_path, notice: I18n.t('morpho.messages.unauthenticated')
|
12
|
+
end
|
4
13
|
end
|
5
14
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Morpho
|
2
|
+
class PasswordsController < ApplicationController
|
3
|
+
skip_before_action :require_login, only: [ :new, :create, :edit, :update ]
|
4
|
+
helper_method :user
|
5
|
+
|
6
|
+
def create
|
7
|
+
if user
|
8
|
+
user.deliver_reset_password_instructions!
|
9
|
+
flash.now[:notice] = I18n.t('morpho.messages.passwords.create.success')
|
10
|
+
render :new
|
11
|
+
else
|
12
|
+
flash.now[:error] = I18n.t('morpho.messages.passwords.create.failure')
|
13
|
+
render :new
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def edit
|
18
|
+
unless user
|
19
|
+
flash.now[:error] = I18n.t('morpho.messages.passwords.edit.failure')
|
20
|
+
render :new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def update
|
25
|
+
if user.update_attributes(password_params)
|
26
|
+
user.change_password!(password_params[:password])
|
27
|
+
redirect_to sign_in_path, success: I18n.t('morpho.messages.passwords.update.success')
|
28
|
+
else
|
29
|
+
flash.now[:error] = I18n.t('morpho.messages.passwords.update.failure')
|
30
|
+
render :edit
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def user
|
37
|
+
@user ||= case
|
38
|
+
when %w(edit update).include?(action_name)
|
39
|
+
Morpho::User.load_from_reset_password_token(params[:token])
|
40
|
+
when %(create).include?(action_name)
|
41
|
+
Morpho::User.find_by(email_params)
|
42
|
+
else
|
43
|
+
Morpho::User.new
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
def email_params
|
49
|
+
params.require(:user).permit(:email)
|
50
|
+
end
|
51
|
+
|
52
|
+
def password_params
|
53
|
+
params.require(:user).permit(:password, :password_confirmation)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Morpho
|
2
|
+
class SessionsController < ApplicationController
|
3
|
+
skip_before_action :require_login, only: [ :new, :create ]
|
4
|
+
|
5
|
+
def create
|
6
|
+
if login(session_params[:email], session_params[:password], session_params[:remember_me])
|
7
|
+
redirect_to root_path, success: I18n.t('morpho.messages.sessions.create.success') and return
|
8
|
+
else
|
9
|
+
flash.now[:error] = I18n.t('morpho.messages.sessions.create.failure')
|
10
|
+
render :new
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def destroy
|
15
|
+
redirect_to sign_in_path, success: I18n.t('morpho.messages.sessions.destroy.success') and return if logout
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
def session_params
|
21
|
+
params.require(:session).permit(:email, :password, :remember_me)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Morpho
|
2
|
+
class UnlocksController < ApplicationController
|
3
|
+
skip_before_action :require_login, only: [ :new, :create, :show ]
|
4
|
+
helper_method :user
|
5
|
+
|
6
|
+
def create
|
7
|
+
if user && user.login_locked?
|
8
|
+
user.resend_unlock_token_email!
|
9
|
+
flash.now[:notice] = I18n.t('morpho.messages.unlocks.unlock.success')
|
10
|
+
render :new
|
11
|
+
else
|
12
|
+
flash.now[:error] = I18n.t('morpho.messages.unlocks.unlock.failure')
|
13
|
+
render :new
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def show
|
18
|
+
if user && user.login_locked?
|
19
|
+
user.login_unlock!
|
20
|
+
redirect_to sign_in_path, success: I18n.t('morpho.messages.unlocks.unlock.success')
|
21
|
+
else
|
22
|
+
redirect_to sign_in_path, error: I18n.t('morpho.messages.unlocks.unlock.failure')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def user
|
29
|
+
@user ||= case
|
30
|
+
when %w(show).include?(action_name)
|
31
|
+
Morpho::User.load_from_unlock_token(params[:token])
|
32
|
+
when %(create).include?(action_name)
|
33
|
+
Morpho::User.find_by(email_params)
|
34
|
+
else
|
35
|
+
Morpho::User.new
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
def email_params
|
41
|
+
params.require(:user).permit(:email)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Morpho
|
2
|
+
class UsersController < ApplicationController
|
3
|
+
skip_before_action :require_login, only: [ :new, :create ]
|
4
|
+
helper_method :user
|
5
|
+
|
6
|
+
def create
|
7
|
+
if user.update_attributes(user_params)
|
8
|
+
redirect_to sign_in_path, success: I18n.t('morpho.messages.users.create.success')
|
9
|
+
else
|
10
|
+
flash.now[:alert] = I18n.t('morpho.messages.users.create.failure')
|
11
|
+
render :new
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def user
|
18
|
+
@user ||= Morpho::User.new
|
19
|
+
end
|
20
|
+
|
21
|
+
def user_params
|
22
|
+
params.require(:user).permit(:email, :password, :password_confirmation)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Morpho
|
2
|
+
class UserMailer < ApplicationMailer
|
3
|
+
def activation_needed_email(user)
|
4
|
+
@user = user
|
5
|
+
@url = activate_url(token: @user.activation_token)
|
6
|
+
|
7
|
+
mail(to: @user.email, subject: t('morpho.mailer.activation_needed.subject'))
|
8
|
+
end
|
9
|
+
|
10
|
+
def activation_success_email(user)
|
11
|
+
@user = user
|
12
|
+
@url = sign_in_url()
|
13
|
+
|
14
|
+
mail(to: @user.email, subject: t('morpho.mailer.activation_success.subject'))
|
15
|
+
end
|
16
|
+
|
17
|
+
def reset_password_email(user)
|
18
|
+
@user = user
|
19
|
+
@url = new_password_url(token: @user.reset_password_token)
|
20
|
+
|
21
|
+
mail(to: @user.email, subject: t('morpho.mailer.reset_password.subject'))
|
22
|
+
end
|
23
|
+
|
24
|
+
def unlock_token_email(user)
|
25
|
+
@user = user
|
26
|
+
@url = unlock_url(token: @user.unlock_token)
|
27
|
+
|
28
|
+
mail(to: @user.email, subject: t('morpho.mailer.unlock_token.subject'))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Morpho
|
2
|
+
class User < ApplicationRecord
|
3
|
+
authenticates_with_sorcery!
|
4
|
+
|
5
|
+
has_many :authentications, dependent: :destroy
|
6
|
+
accepts_nested_attributes_for :authentications
|
7
|
+
|
8
|
+
validates :password, length: { minimum: 12 }
|
9
|
+
validates :password, confirmation: true
|
10
|
+
validates :email, uniqueness: true
|
11
|
+
validates_email_format_of :email
|
12
|
+
|
13
|
+
def active?
|
14
|
+
self.activation_state == 'active'
|
15
|
+
end
|
16
|
+
|
17
|
+
def resend_activation_needed_email!
|
18
|
+
self.setup_activation
|
19
|
+
self.reload
|
20
|
+
self.send_activation_needed_email!
|
21
|
+
end
|
22
|
+
|
23
|
+
def resend_unlock_token_email!
|
24
|
+
self.login_lock!
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# bugfix: https://github.com/Sorcery/sorcery/issues/146
|
29
|
+
def create_and_validate_from_provider(provider, uid, attrs)
|
30
|
+
user = new(attrs)
|
31
|
+
|
32
|
+
user.send(sorcery_config.authentications_class.name.demodulize.underscore.pluralize).build(
|
33
|
+
sorcery_config.provider_uid_attribute_name => uid,
|
34
|
+
sorcery_config.provider_attribute_name => provider
|
35
|
+
)
|
36
|
+
|
37
|
+
saved = user.sorcery_adapter.save
|
38
|
+
[user, saved]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# bugfix: https://github.com/Sorcery/sorcery/issues/146
|
43
|
+
def add_provider_to_user(provider, uid)
|
44
|
+
authentications = sorcery_config.authentications_class.name.demodulize.underscore.pluralize
|
45
|
+
|
46
|
+
if sorcery_adapter.find_authentication_by_oauth_credentials(authentications, provider, uid).nil?
|
47
|
+
user = send(authentications).build(
|
48
|
+
sorcery_config.provider_uid_attribute_name => uid,
|
49
|
+
sorcery_config.provider_attribute_name => provider
|
50
|
+
)
|
51
|
+
|
52
|
+
user.sorcery_adapter.save(validate: false)
|
53
|
+
else
|
54
|
+
user = false
|
55
|
+
end
|
56
|
+
|
57
|
+
user
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -5,12 +5,14 @@
|
|
5
5
|
<%= csrf_meta_tags %>
|
6
6
|
<%= csp_meta_tag %>
|
7
7
|
|
8
|
-
<%= stylesheet_link_tag
|
9
|
-
<%= javascript_include_tag
|
8
|
+
<%= stylesheet_link_tag 'morpho/application', media: 'all' %>
|
9
|
+
<%= javascript_include_tag 'morpho/application' %>
|
10
10
|
</head>
|
11
11
|
<body>
|
12
|
-
|
13
|
-
<%=
|
12
|
+
<div class="container">
|
13
|
+
<%= render_flash_messages %>
|
14
|
+
<%= yield %>
|
15
|
+
</div>
|
14
16
|
|
15
17
|
</body>
|
16
18
|
</html>
|