diff --git a/Gemfile b/Gemfile index 633a219..1de8b75 100644 --- a/Gemfile +++ b/Gemfile @@ -44,6 +44,8 @@ group :development do # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' gem 'spring-watcher-listen', '~> 2.0.0' + gem 'letter_opener' + gem 'letter_opener_web' end group :test do diff --git a/Gemfile.lock b/Gemfile.lock index 266dfba..9dcd176 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -97,6 +97,14 @@ GEM concurrent-ruby (~> 1.0) jbuilder (2.10.1) activesupport (>= 5.0.0) + launchy (2.4.3) + addressable (~> 2.3) + letter_opener (1.7.0) + launchy (~> 2.2) + letter_opener_web (1.3.4) + actionmailer (>= 3.2) + letter_opener (~> 1.0) + railties (>= 3.2) listen (3.2.1) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -237,6 +245,8 @@ DEPENDENCIES devise_ldap_authenticatable dotenv-rails jbuilder (~> 2.7) + letter_opener + letter_opener_web listen (~> 3.2) net-ldap puma (~> 4.1) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 09705d1..19434cd 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,2 +1,5 @@ class ApplicationController < ActionController::Base + rescue_from DeviseLdapAuthenticatable::LdapException do |exception| + render :text => exception, :status => 500 + end end diff --git a/app/controllers/devise/passwords_controller.rb b/app/controllers/devise/passwords_controller.rb new file mode 100644 index 0000000..adf163e --- /dev/null +++ b/app/controllers/devise/passwords_controller.rb @@ -0,0 +1,83 @@ +# frozen_string_literal: true + +class Devise::PasswordsController < DeviseController + prepend_before_action :require_no_authentication + # Render the #edit only if coming from a reset password email link + append_before_action :assert_reset_token_passed, only: :edit + + # GET /resource/password/new + def new + self.resource = resource_class.new + end + + # POST /resource/password + def create + self.resource = resource_class.send_reset_password_instructions(resource_params) + yield resource if block_given? + + if successfully_sent?(resource) + respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name)) + else + respond_with(resource) + end + end + + # GET /resource/password/edit?reset_password_token=abcdef + def edit + self.resource = resource_class.new + set_minimum_password_length + resource.reset_password_token = params[:reset_password_token] + end + + # PUT /resource/password + def update + self.resource = resource_class.reset_password_by_token(resource_params) + yield resource if block_given? + + if resource.errors.empty? + resource.unlock_access! if unlockable?(resource) + if Devise.sign_in_after_reset_password + flash_message = resource.active_for_authentication? ? :updated : :updated_not_active + set_flash_message!(:notice, flash_message) + resource.after_ldap_authentication + sign_in(resource_name, resource) + else + set_flash_message!(:notice, :updated_not_active) + end + respond_with resource, location: after_resetting_password_path_for(resource) + else + set_minimum_password_length + respond_with resource + end + end + + protected + def after_resetting_password_path_for(resource) + Devise.sign_in_after_reset_password ? after_sign_in_path_for(resource) : new_session_path(resource_name) + end + + # The path used after sending reset password instructions + def after_sending_reset_password_instructions_path_for(resource_name) + new_session_path(resource_name) if is_navigational_format? + end + + # Check if a reset_password_token is provided in the request + def assert_reset_token_passed + if params[:reset_password_token].blank? + set_flash_message(:alert, :no_token) + redirect_to new_session_path(resource_name) + end + end + + # Check if proper Lockable module methods are present & unlock strategy + # allows to unlock resource on password reset + def unlockable?(resource) + resource.respond_to?(:unlock_access!) && + resource.respond_to?(:unlock_strategy_enabled?) && + resource.unlock_strategy_enabled?(:email) + end + + def translation_scope + 'devise.passwords' + end +end diff --git a/app/controllers/ldap_users_controller.rb b/app/controllers/ldap_users_controller.rb index b7f4f6d..4124b3e 100644 --- a/app/controllers/ldap_users_controller.rb +++ b/app/controllers/ldap_users_controller.rb @@ -16,6 +16,7 @@ class LdapUsersController < ApplicationController uid: e.uid.first, mail: e.try(:mail) ? e.mail.first : nil, admin: e.try(:admin) ? 'admin' : nil + # password: e.userpassword.first } end # ldap_client.get_operation_result @@ -25,12 +26,16 @@ class LdapUsersController < ApplicationController def ldap_client ldap_client ||= Net::LDAP.new host: ENV['LDAP_HOST'], - port: ENV['LDAP_PORT'], - encryption: ENV['LDAP_USE_TLS'] ? :simple_tls : nil, + port: ldap_config['port'], + encryption: ldap_config['ssl'], auth: { method: :simple, - username: Rails.application.credentials.ldap[:username], - password: Rails.application.credentials.ldap[:password] + username: ldap_config['admin_user'], + password: ldap_config['admin_password'] } end + + def ldap_config + ldap_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/ldap.yml")).result)[Rails.env] + end end diff --git a/app/controllers/settings_controller.rb b/app/controllers/settings_controller.rb new file mode 100644 index 0000000..b9adfb9 --- /dev/null +++ b/app/controllers/settings_controller.rb @@ -0,0 +1,4 @@ +class SettingsController < ApplicationController + def index + end +end diff --git a/app/controllers/welcome_controller.rb b/app/controllers/welcome_controller.rb index f9b859b..5aaad05 100644 --- a/app/controllers/welcome_controller.rb +++ b/app/controllers/welcome_controller.rb @@ -1,4 +1,7 @@ class WelcomeController < ApplicationController def index + if user_signed_in? + redirect_to settings_path and return + end end end diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb new file mode 100644 index 0000000..ffbedba --- /dev/null +++ b/app/helpers/settings_helper.rb @@ -0,0 +1,2 @@ +module SettingsHelper +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..2682230 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,23 @@ +class User < ApplicationRecord + # Include default devise modules. Others available are: + # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable + devise :ldap_authenticatable, + :confirmable, + :registerable, + :recoverable, + :validatable + + def ldap_before_save + self.email = Devise::LDAP::Adapter.get_ldap_param(self.cn, "mail").first + dn = Devise::LDAP::Adapter.get_ldap_param(self.cn, "dn") + self.ou = dn.split(',').select{|e| e[0..1] == "ou"}.first.delete_prefix("ou=") + end + + def reset_password(new_password, new_password_confirmation) + if new_password == new_password_confirmation && ::Devise.ldap_update_password + Devise::LDAP::Adapter.update_password(login_with, new_password) + end + clear_reset_password_token if valid? + save + end +end diff --git a/app/views/devise/confirmations/new.html.erb b/app/views/devise/confirmations/new.html.erb new file mode 100644 index 0000000..b12dd0c --- /dev/null +++ b/app/views/devise/confirmations/new.html.erb @@ -0,0 +1,16 @@ +
Welcome <%= @email %>!
+ +You can confirm your account email through the link below:
+ +<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
diff --git a/app/views/devise/mailer/email_changed.html.erb b/app/views/devise/mailer/email_changed.html.erb new file mode 100644 index 0000000..32f4ba8 --- /dev/null +++ b/app/views/devise/mailer/email_changed.html.erb @@ -0,0 +1,7 @@ +Hello <%= @email %>!
+ +<% if @resource.try(:unconfirmed_email?) %> +We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.
+<% else %> +We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
+<% end %> diff --git a/app/views/devise/mailer/password_change.html.erb b/app/views/devise/mailer/password_change.html.erb new file mode 100644 index 0000000..b41daf4 --- /dev/null +++ b/app/views/devise/mailer/password_change.html.erb @@ -0,0 +1,3 @@ +Hello <%= @resource.email %>!
+ +We're contacting you to notify you that your password has been changed.
diff --git a/app/views/devise/mailer/reset_password_instructions.html.erb b/app/views/devise/mailer/reset_password_instructions.html.erb new file mode 100644 index 0000000..f667dc1 --- /dev/null +++ b/app/views/devise/mailer/reset_password_instructions.html.erb @@ -0,0 +1,8 @@ +Hello <%= @resource.email %>!
+ +Someone has requested a link to change your password. You can do this through the link below.
+ +<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>
+ +If you didn't request this, please ignore this email.
+Your password won't change until you access the link above and create a new one.
diff --git a/app/views/devise/mailer/unlock_instructions.html.erb b/app/views/devise/mailer/unlock_instructions.html.erb new file mode 100644 index 0000000..41e148b --- /dev/null +++ b/app/views/devise/mailer/unlock_instructions.html.erb @@ -0,0 +1,7 @@ +Hello <%= @resource.email %>!
+ +Your account has been locked due to an excessive number of unsuccessful sign in attempts.
+ +Click the link below to unlock your account:
+ +<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>
diff --git a/app/views/devise/passwords/edit.html.erb b/app/views/devise/passwords/edit.html.erb new file mode 100644 index 0000000..5fbb9ff --- /dev/null +++ b/app/views/devise/passwords/edit.html.erb @@ -0,0 +1,25 @@ +Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>
+ +<%= link_to "Back", :back %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb new file mode 100644 index 0000000..d655b66 --- /dev/null +++ b/app/views/devise/registrations/new.html.erb @@ -0,0 +1,29 @@ ++ Signed in as <%= current_user.cn %>@kosmos.org. + <%= link_to "Log out", destroy_user_session_path, method: :delete %> +
+ <% flash.each do |type, msg| %> +<%= msg %>
+ <% end %> + <% end %> + <%= yield %>