parent
a14385cbd2
commit
879f436c2a
@ -0,0 +1,2 @@ |
||||
FROM redmine |
||||
COPY ./redmine_ldap_passwd /usr/share/redmine/plugins/ |
@ -0,0 +1,27 @@ |
||||
# Redmine LDAP Passwd plugin >= Redmine 3.0 |
||||
|
||||
The plugin extends AuthSourceLdap to introduce the ability to recover or change user password. |
||||
|
||||
### Features |
||||
|
||||
* Allows to changed password and update LDAP record. |
||||
* Allows to recover password and update LDAP record. |
||||
|
||||
**Notes** |
||||
|
||||
* The solution has been tested on MS Active Directory only. It works only with SSL connection, please ensure SSL is configured on Active Directory side. |
||||
|
||||
### Install |
||||
|
||||
1. Follow Redmine [plugin installation instructions](http://www.redmine.org/projects/redmine/wiki/Plugins#Installing-a-plugin). |
||||
2. Add new LDAP connection and check the records in 'auth_sources' making sure column 'type'='AuthSourceLdapPasswd'. If it is not, update the record manually executing the SQL query. |
||||
3. Assign new LDAP connection to the specific users you would like to provide access through LDAP to. |
||||
|
||||
### Uninstall |
||||
|
||||
1. Follow Redmine [plugin uninstall instructions](http://www.redmine.org/projects/redmine/wiki/Plugins#Uninstalling-a-plugin). |
||||
|
||||
### Changelog |
||||
|
||||
* **3.0 (2016-05-31)** |
||||
* Initial version released. |
@ -0,0 +1,57 @@ |
||||
class AuthSourceLdapPasswd < AuthSourceLdap |
||||
def allow_password_changes? |
||||
self.tls |
||||
end |
||||
|
||||
def change_user_password(user, password, new_password) |
||||
return false unless AuthSourceLdapPasswd.change_password_allowed?(user) |
||||
|
||||
attrs = get_user_dn(user.login, password) |
||||
if attrs && attrs[:dn] |
||||
if self.account && self.account.include?("$login") |
||||
ldap_con = initialize_ldap_con(self.account.sub("$login", Net::LDAP::DN.escape(user.login)), password) |
||||
else |
||||
ldap_con = initialize_ldap_con(self.account, self.account_password) |
||||
end |
||||
|
||||
ops = [[:replace, :unicodePwd, AuthSourceLdapPasswd.str2unicodePwd(new_password)]] |
||||
ldap_con.modify :dn => attrs[:dn], :operations => ops |
||||
|
||||
result = ldap_con.get_operation_result |
||||
if result.code == 0 |
||||
user.passwd_changed_on = Time.now.change(:usec => 0) |
||||
user.save |
||||
|
||||
return true |
||||
else |
||||
return result |
||||
end |
||||
end |
||||
|
||||
false |
||||
end |
||||
|
||||
def self.str2unicodePwd(str) |
||||
('"' + str + '"').encode("utf-16le").force_encoding("utf-8") |
||||
end |
||||
|
||||
def self.change_password_allowed?(user) |
||||
return false if user.nil? |
||||
AuthSourceLdapPasswd.name.eql?(user.auth_source.type) |
||||
end |
||||
|
||||
def self.is_password_valid(password) |
||||
return false if password.nil? || password.length < 7 |
||||
|
||||
s = 0 |
||||
contains = [ |
||||
password.match(/\p{Lower}/) ? 1 : 0, |
||||
password.match(/\p{Upper}/) ? 1 : 0, |
||||
password.match(/\p{Digit}/) ? 1 : 0, |
||||
password.match(/[^\\w\\d]+/) ? 1 : 0 |
||||
] |
||||
contains.each { |a| s += a } |
||||
|
||||
return s >= 3 |
||||
end |
||||
end |
@ -0,0 +1,3 @@ |
||||
en: |
||||
notice_new_password_and_confirmation_different: The new password is different from the confirmation password |
||||
notice_new_password_format: "1. The password should be at least seven characters long. 2. The password should contain characters from at least three of the following four categories: (a) English uppercase characters (A - Z) (b) English lowercase characters (a - z) (c) Base 10 digits (0 - 9) (d) Non-alphanumeric (For example: !, $, or %). 3. The password shouldn't contain three or more characters from the user's account name." |
@ -0,0 +1,36 @@ |
||||
require 'redmine' |
||||
|
||||
require_dependency 'redmine_ldap_passwd_my_controller_patch' |
||||
require_dependency 'redmine_ldap_passwd_auth_sources_helper_patch' |
||||
require_dependency 'redmine_ldap_passwd_account_controller_patch' |
||||
|
||||
Redmine::Plugin.register :redmine_ldap_passwd do |
||||
name 'Redmine LDAP Change Password' |
||||
author 'Yura Zaplavnov' |
||||
description 'The plugin extends AuthSourceLdap to introduce the ability to recover or change user password.' |
||||
version '3.0.1' |
||||
url 'https://github.com/xeagle2/redmine_ldap_passwd' |
||||
author_url 'https://github.com/xeagle2' |
||||
end |
||||
|
||||
require 'dispatcher' unless Rails::VERSION::MAJOR >= 3 |
||||
|
||||
if Rails::VERSION::MAJOR >= 5 |
||||
ActiveSupport::Reloader.to_prepare do |
||||
MyController.send(:include, RedmineLdapPasswd::MyControllerPatch) |
||||
AuthSourcesHelper.send(:include, RedmineLdapPasswd::AuthSourcesHelperPatch) |
||||
AccountController.send(:include, RedmineLdapPasswd::AccountControllerPatch) |
||||
end |
||||
elsif Rails::VERSION::MAJOR >= 3 |
||||
ActionDispatch::Callbacks.to_prepare do |
||||
MyController.send(:include, RedmineLdapPasswd::MyControllerPatch) |
||||
AuthSourcesHelper.send(:include, RedmineLdapPasswd::AuthSourcesHelperPatch) |
||||
AccountController.send(:include, RedmineLdapPasswd::AccountControllerPatch) |
||||
end |
||||
else |
||||
Dispatcher.to_prepare do |
||||
MyController.send(:include, RedmineLdapPasswd::MyControllerPatch) |
||||
AuthSourcesHelper.send(:include, RedmineLdapPasswd::AuthSourcesHelperPatch) |
||||
AccountController.send(:include, RedmineLdapPasswd::AccountControllerPatch) |
||||
end |
||||
end |
@ -0,0 +1,66 @@ |
||||
module RedmineLdapPasswd |
||||
module AccountControllerPatch |
||||
def self.included(base) |
||||
base.send(:extend, ClassMethods) |
||||
base.send(:include, InstanceMethods) |
||||
|
||||
base.class_eval do |
||||
unloadable # Send unloadable so it will not be unloaded in development |
||||
|
||||
if Rails::VERSION::MAJOR >= 5 |
||||
alias_method :lost_password_without_extension, :lost_password |
||||
alias_method :lost_password, :lost_password_with_extension |
||||
else |
||||
alias_method :lost_password, :extension |
||||
end |
||||
end |
||||
end |
||||
|
||||
module ClassMethods |
||||
end |
||||
|
||||
module InstanceMethods |
||||
def lost_password_with_extension |
||||
if params[:token] |
||||
@token = Token.find_token("recovery", params[:token].to_s) |
||||
if @token.nil? || @token.expired? |
||||
redirect_to home_url |
||||
return |
||||
end |
||||
|
||||
@user = @token.user |
||||
unless @user && @user.active? |
||||
redirect_to home_url |
||||
return |
||||
end |
||||
|
||||
if request.post? |
||||
if params[:new_password_confirmation] != params[:new_password] |
||||
flash.now[:error] = l(:notice_new_password_and_confirmation_different) |
||||
elsif !AuthSourceLdapPasswd.is_password_valid (params[:new_password]) |
||||
flash.now[:error] = l(:notice_new_password_format) |
||||
else |
||||
r = @user.auth_source.change_user_password(@user, '', params[:new_password]) |
||||
|
||||
if r == true |
||||
flash[:notice] = l(:notice_account_password_updated) |
||||
redirect_to signin_path |
||||
elsif r == false |
||||
lost_password_without_extension |
||||
else |
||||
flash.now[:error] = r.message |
||||
end |
||||
|
||||
return |
||||
end |
||||
end |
||||
|
||||
render :template => "account/password_recovery" |
||||
return |
||||
else |
||||
lost_password_without_extension |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,25 @@ |
||||
module RedmineLdapPasswd |
||||
module AuthSourcesHelperPatch |
||||
def self.included(base) # :nodoc: |
||||
base.send(:include, InstanceMethods) |
||||
|
||||
base.class_eval do |
||||
unloadable # Send unloadable so it will not be unloaded in development |
||||
|
||||
if Rails::VERSION::MAJOR >= 5 |
||||
alias_method :auth_source_partial_name_without_ignored_passwd, :auth_source_partial_name |
||||
alias_method :auth_source_partial_name, :auth_source_partial_name_with_ignored_passwd |
||||
else |
||||
alias_method :auth_source_partial_name, :ignored_passwd |
||||
end |
||||
end |
||||
end |
||||
|
||||
module InstanceMethods |
||||
# Make sure AuthSourceLdapPasswd is loaded with the same form as AuthSourceLdap |
||||
def auth_source_partial_name_with_ignored_passwd(auth_source) |
||||
"form_#{auth_source.class.name.underscore}".chomp('_passwd') |
||||
end |
||||
end |
||||
end |
||||
end |
@ -0,0 +1,64 @@ |
||||
module RedmineLdapPasswd |
||||
module MyControllerPatch |
||||
def self.included(base) |
||||
base.send(:extend, ClassMethods) |
||||
base.send(:include, InstanceMethods) |
||||
|
||||
base.class_eval do |
||||
unloadable # Send unloadable so it will not be unloaded in development |
||||
|
||||
if Rails::VERSION::MAJOR >= 5 |
||||
alias_method :password_without_extension, :password |
||||
alias_method :password, :password_with_extension |
||||
else |
||||
alias_method_chain :password, :extension |
||||
end |
||||
end |
||||
end |
||||
|
||||
module ClassMethods |
||||
end |
||||
|
||||
module InstanceMethods |
||||
def password_with_extension |
||||
@user = User.current |
||||
|
||||
unless @user.change_password_allowed? |
||||
flash[:error] = l(:notice_can_t_change_password) |
||||
redirect_to my_account_path |
||||
return |
||||
end |
||||
|
||||
if request.post? |
||||
if !@user.check_password?(params[:password]) |
||||
flash.now[:error] = l(:notice_account_wrong_password) |
||||
elsif params[:password] == params[:new_password] |
||||
flash.now[:error] = l(:notice_new_password_must_be_different) |
||||
elsif params[:new_password_confirmation] != params[:new_password] |
||||
flash.now[:error] = l(:notice_new_password_and_confirmation_different) |
||||
elsif AuthSourceLdapPasswd.change_password_allowed?(@user) |
||||
if AuthSourceLdapPasswd.is_password_valid (params[:new_password]) |
||||
r = @user.auth_source.change_user_password(@user, params[:password], params[:new_password]) |
||||
|
||||
if r == true |
||||
session[:ctime] = User.current.passwd_changed_on.utc.to_i |
||||
flash[:notice] = l(:notice_account_password_updated) |
||||
redirect_to my_account_path |
||||
elsif r == false |
||||
password_without_extension |
||||
else |
||||
flash.now[:error] = r.message |
||||
end |
||||
else |
||||
flash.now[:error] = l(:notice_new_password_format) |
||||
end |
||||
else |
||||
password_without_extension |
||||
end |
||||
end |
||||
rescue Net::LDAP::LdapError => e |
||||
raise AuthSourceException.new(e.message) |
||||
end |
||||
end |
||||
end |
||||
end |
Loading…
Reference in new issue