force ldap authentication only (deny internal auth) possible?
Added by Iancu Stoica over 10 years ago
Hello,
I have been searching the web for half a day on this topic without too much success, so I am posting here in hopes of finding a solution.
The company I work for has been deploying redmine instances for a lot of projects. The reasoning is we provide the instance and give administrator rights to the project leader, and afterwards he/she can manage and customize the instance as they see fit.
All is working nicely but now we want to integrate all redmine instances with our central Active Directory. LDAP authentication works fine. However, when an administrator creates a new user (we want the project managers to add their users to the redmine instance), the authentication type cannot be locked down to ldap and this is a problem for us. We don't want to use the "on the fly" option and we don't want to leave the "internal" option available, thus forcing the admins to add only ldap users to their redmine instance.
Any advice would be greatly appreciated.
Environment:
-each redmine instance is deployed on it's own vm; most of them are on suse linux enterprise 11 sp1 x64, but we also have some ubuntu server 12.04, and fedora 13
-the redmine versions vary from 1.x to the latest version
-database engine is mysql on all of them
Best regards,
Iancu
Replies (3)
RE: force ldap authentication only (deny internal auth) possible? - Added by Martin Denizet (redmine.org team member) over 10 years ago
Hi Iancu,
I think the solution would be to do a plugin to wrap and break the internal authentication.
I think these changes should do the trick, in source:trunk/app/models/user.rb
# Returns the user that matches provided login and password, or nil def self.try_to_login(login, password, active_only=true) login = login.to_s password = password.to_s # Make sure no one can sign in with an empty login or password return nil if login.empty? || password.empty? user = find_by_login(login) if user # user is already in local database return nil unless user.check_password?(password) return nil if !user.active? && active_only else - # user is not yet registered, try to authenticate with available sources - attrs = AuthSource.authenticate(login, password) - if attrs - user = new(attrs) - user.login = login - user.language = Setting.default_language - if user.save - user.reload - logger.info("User '#{user.login}' created from external auth source: #{user.auth_source.type} - #{user.auth_source.name}") if logger && user.auth_source - end - end + return nil end user.update_column(:last_login_on, Time.now) if user && !user.new_record? && user.active? user rescue => text raise text end
# Returns true if +clear_password+ is the correct user's password, otherwise false def check_password?(clear_password) if auth_source_id.present? auth_source.authenticate(self.login, clear_password) else - User.hash_password("#{salt}#{User.hash_password clear_password}") == hashed_password + return false end end
Again, don't edit the source but wrap the method with a plugin. It's much cleaner.
Cheers,
RE: force ldap authentication only (deny internal auth) possible? - Added by Iancu Stoica over 10 years ago
Dear Martin,
Thank you very much for the provided solution. I have tested it, and although it is not ideal, it works and does solve our issue - an admin can create local accounts, but no local account can login.
Could something be done so that the actual action of creating internal accounts be blocked?
Thanks again,
Iancu
RE: force ldap authentication only (deny internal auth) possible? - Added by Martin Denizet (redmine.org team member) over 10 years ago
I see 2 options:
1. Wrap the controller¶
1.1 Remove the possibility to set manually the auth_source, in source:trunk/app/models/user.rb
safe_attributes 'status', - 'auth_source_id', 'generate_password', 'must_change_passwd', :if => lambda {|user, current_user| current_user.admin?}
1.2 Wrap the controller, in source:trunk/app/controllers/users_controller.rb
def create @user = User.new(:language => Setting.default_language, :mail_notification => Setting.default_notification_option) @user.safe_attributes = params[:user] + #Enforce the first auth source ID in the DB + @user.auth_source_id = AuthSource.first @user.admin = params[:user][:admin] || false @user.login = params[:user][:login] - @user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] unless @user.auth_source_id @user.pref.attributes = params[:pref] if @user.save Mailer.account_information(@user, @user.password).deliver if params[:send_information] respond_to do |format| format.html { flash[:notice] = l(:notice_user_successful_create, :id => view_context.link_to(@user.login, user_path(@user))) if params[:continue] attrs = params[:user].slice(:generate_password) redirect_to new_user_path(:user => attrs) else redirect_to edit_user_path(@user) end } format.api { render :action => 'show', :status => :created, :location => user_url(@user) } end else @auth_sources = AuthSource.all # Clear password input @user.password = @user.password_confirmation = nil respond_to do |format| format.html { render :action => 'new' } format.api { render_validation_errors(@user) } end end end
2. Wrap the controller to display an error message if the auth source is internal¶
Chain the create
method in source:trunk/app/controllers/users_controller.rb with "limited_source", such as:
def create_with_limited_source if params[:user][:auth_source_id].nil? flash[:error] = "We do not allow internal authentication!" respond_to do |format| format.html { render :action => 'new' } end else create_without_limited_source end end
After thinking about it a little, I like the second option better. And I also realized there is a third option that sounds event better which is to have a validator in the model to prevent user.auth_source_id = nil
(validate presence of auth_source_id)
Cheers,