Merge pull request #239 from opf/feature/rails3_random_password

Feature/rails3 random password
pull/242/merge
Philipp Tessenow 11 years ago
commit 277fe872cc
  1. 29
      app/assets/javascripts/admin_users.js
  2. 24
      app/controllers/users_controller.rb
  3. 7
      app/models/user.rb
  4. 29
      app/views/users/_form.html.erb
  5. 6
      app/views/users/_random_password.html.erb
  6. 1
      config/locales/de.yml
  7. 1
      config/locales/en.yml
  8. 2
      doc/CHANGELOG.md
  9. 14
      features/step_definitions/email_steps.rb
  10. 23
      features/step_definitions/password_steps.rb
  11. 35
      features/step_definitions/user_steps.rb
  12. 37
      features/users/random_password_assignment.feature
  13. 1
      spec/models/user_spec.rb
  14. 6
      test/unit/mail_handler_test.rb

@ -0,0 +1,29 @@
(function() {
// When 'assign random password' field is enabled,
// disable and clear password fields and disable and check the
// 'force password reset' field
function on_assign_random_password_change(){
var checked = jQuery('#user_assign_random_password').is(':checked');
jQuery('#user_password').prop('disabled', checked);
jQuery('#user_password_confirmation').prop('disabled', checked);
jQuery('#user_password').val('');
jQuery('#user_password_confirmation').val('');
jQuery('#user_force_password_change').prop('checked', checked)
.prop('disabled', checked);
}
// Hide password fields when non-internal authentication source is selected
function on_auth_source_change() {
if (this.value === '') {
jQuery('#password_fields').show();
} else {
jQuery('#password_fields').hide();
}
}
jQuery(function init(){
jQuery('#user_assign_random_password').change(on_assign_random_password_change);
jQuery('#user_auth_source_id').change(on_auth_source_change);
});
})();

@ -101,7 +101,14 @@ class UsersController < ApplicationController
@user.safe_attributes = params[:user]
@user.admin = params[:user][:admin] || false
@user.login = params[:user][:login]
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation] if @user.change_password_allowed?
if @user.change_password_allowed?
if params[:user][:assign_random_password]
@user.random_password!
else
@user.password = params[:user][:password]
@user.password_confirmation = params[:user][:password_confirmation]
end
end
if @user.save
# TODO: Similar to My#account
@ -111,7 +118,7 @@ class UsersController < ApplicationController
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
UserMailer.account_information(@user, params[:user][:password]).deliver if params[:send_information]
UserMailer.account_information(@user, @user.password).deliver if params[:send_information]
respond_to do |format|
format.html {
@ -143,8 +150,13 @@ class UsersController < ApplicationController
@user.admin = params[:user][:admin] if params[:user][:admin]
@user.login = params[:user][:login] if params[:user][:login]
@user.attributes = permitted_params.user_update_as_admin
if params[:user][:password].present? && @user.change_password_allowed?
@user.password, @user.password_confirmation = params[:user][:password], params[:user][:password_confirmation]
if @user.change_password_allowed?
if params[:user][:assign_random_password]
@user.random_password!
elsif params[:user][:password].present?
@user.password = params[:user][:password]
@user.password_confirmation = params[:user][:password_confirmation]
end
end
if @user.save
@ -155,8 +167,8 @@ class UsersController < ApplicationController
@user.notified_project_ids = (@user.mail_notification == 'selected' ? params[:notified_project_ids] : [])
if @user.active? && params[:send_information] && !params[:user][:password].blank? && @user.change_password_allowed?
UserMailer.account_information(@user, params[:user][:password]).deliver
if @user.active? && params[:send_information] && !@user.password.blank? && @user.change_password_allowed?
UserMailer.account_information(@user, @user.password).deliver
end
respond_to do |format|

@ -341,10 +341,17 @@ class User < Principal
return auth_source.allow_password_changes?
end
#
# Generate and set a random password.
#
# Also force a password change on the next login, since random passwords
# are at some point given to the user, we do this via email. These passwords
# are stored unencrypted in mail accounts, so they must only be valid for
# a short time.
def random_password!
self.password = OpenProject::Passwords::Generator.random_password
self.password_confirmation = self.password
self.force_password_change = true
self
end

@ -12,6 +12,8 @@ See doc/COPYRIGHT.rdoc for more details.
<%= error_messages_for 'user' %>
<%= javascript_include_tag 'admin_users' %>
<!--[form:user]-->
<div class="box tabular">
<p><%= f.text_field :login, :required => true, :size => 25 %></p>
@ -39,16 +41,35 @@ See doc/COPYRIGHT.rdoc for more details.
<div class="box tabular">
<h3><%=l(:label_authentication)%></h3>
<% unless @auth_sources.empty? %>
<p><%= f.select :auth_source_id, ([[l(:label_internal), ""]] + @auth_sources.collect { |a| [a.name, a.id] }), {}, :onchange => "if (this.value=='') {Element.show('password_fields');} else {Element.hide('password_fields');}" %></p>
<p><%= f.select :auth_source_id, ([[l(:label_internal), ""]] + @auth_sources.collect { |a| [a.name, a.id] }) %></p>
<% end %>
<div id="password_fields" style="<%= 'display:none;' if @user.auth_source %>">
<% assign_random_password_enabled = params[:user] &&
params[:user][:assign_random_password] %>
<p>
<label for="user_assign_random_password">
<%= I18n.t(:assign_random_password, :scope => :user) %>
</label>
<%= check_box_tag("user[assign_random_password]",
"1",
assign_random_password_enabled) %>
</p>
<p>
<%= f.password_field :password, :required => true, :size => 25 %><br />
<%= f.password_field :password,
:required => true,
:size => 25,
:disabled => assign_random_password_enabled %><br />
<%= password_complexity_requirements %>
</p>
<p><%= f.password_field :password_confirmation, :required => true, :size => 25 %></p>
<p>
<%= f.check_box :force_password_change %>
<%= f.password_field :password_confirmation,
:required => true,
:size => 25,
:disabled => assign_random_password_enabled %>
</p>
<p>
<%= f.check_box :force_password_change,
:disabled => assign_random_password_enabled %>
</p>
</div>
</div>

@ -0,0 +1,6 @@
<p>
<label for="set_random_password">
<%=l(:field_random_password)%>
</label>
<%= check_box_tag("set_random_password", 1, (params[:user] && params[:set_random_password])) %>
</p>

@ -1536,6 +1536,7 @@ de:
active: "aktiv"
activate: "Aktivieren"
activate_and_reset_failed_logins: "Aktivieren und fehlgeschlagene Logins zurücksetzen"
assign_random_password: "Zufälliges Passwort zuweisen (wird dem Nutzer per E-Mail geschickt)"
blocked: "temporär gesperrt"
blocked_num_failed_logins:
one: "temporär gesperrt (ein fehlgeschlagener Loginversuch)"

@ -1515,6 +1515,7 @@ en:
active: "active"
activate: "Activate"
activate_and_reset_failed_logins: "Activate and reset failed logins"
assign_random_password: "Assign random password (sent to user via e-mail)"
blocked: "locked temporarily"
blocked_num_failed_logins:
one: "locked temporarily (one failed login attempt)"

@ -1,5 +1,7 @@
# Changelog
* `#1024` Add 'assign random password' option to user settings
## 3.0.0pre7
* `#1119` Creates a unified view for work_package show, new and create

@ -0,0 +1,14 @@
def last_email
ActionMailer::Base.deliveries.last
end
def assigned_password_from_last_email
last_email.text_part.body.to_s.match(/Password: (.+)$/)[1]
end
Then /^an e-mail should be sent containing "([^\"]*)"$/ do |content|
# An e-mail should always have a text representation, so check
# whether it contains the expected content
last_email.text_part.body.should include(content)
end

@ -74,6 +74,24 @@ Then /^I should be able to login using the new password$/ do
login(@user.login, @new_password)
end
Then /^the password and confirmation fields should be empty$/ do
find('#user_password').value.should be_empty
find('#user_password_confirmation').value.should be_empty
end
Then /^the password and confirmation fields should be disabled$/ do
find('#user_password').should be_disabled
find('#user_password_confirmation').should be_disabled
end
Then /^the force password change field should be checked$/ do
find('#user_force_password_change').should be_checked
end
Then /^the force password change field should be disabled$/ do
find('#user_force_password_change').should be_disabled
end
Given /^I try to log in with user "([^"]*)"$/ do |login|
step 'I go to the logout page'
login(login, @new_password || 'adminADMIN!')
@ -84,6 +102,11 @@ Given /^I try to log in with user "([^"]*)" and a wrong password$/ do |login|
login(login, 'Wrong password')
end
Given /^I try to log in with user "([^"]*)" and the password sent via email$/ do |login|
step 'I go to the logout page'
login(login, assigned_password_from_last_email)
end
When /^I activate the ([a-z, ]+) password rules$/ do |rules|
rules = parse_password_rules(rules)
# ensure checkboxes are loaded, 'all' doesn't wait

@ -9,18 +9,43 @@
# See doc/COPYRIGHT.rdoc for more details.
#++
##
# Editing/creating users (admin UI)
#
When /^I create a new user$/ do
visit '/users/new'
fill_in('user_login', :with => 'newbobby')
fill_in('user_firstname', :with => 'newbobby')
fill_in('user_lastname', :with => 'newbobby')
fill_in('user_mail', :with => 'newbobby@example.com')
end
When /^I edit the user "([^\"]*)"$/ do |user|
user_id = User.find_by_login(user).id
visit "/users/#{user_id}/edit"
end
When /^I (activate_and_reset_failed_logins|lock|unlock) the user "([^\"]*)"$/ do |action, user|
button_title = {
'activate' => 'Unlock and reset failed logins'
}
click_button()
When /^I assign the user "([^\"]*)" a random password$/ do |user|
step "I edit the user \"#{user}\""
step "I check the assign random password to user field"
step "I save the user"
end
When /^I check the assign random password to user field$/ do
check(I18n.t(:assign_random_password, :scope => :user))
end
Given /^I save the user$/ do
click_button('Save', :visible => true)
end
Given /^I save the new user$/ do
find('input[name=commit]').click
end
##
# Creating users (on the DB)
#
Given /^there is 1 [Uu]ser with(?: the following)?:$/ do |table|
login = table.rows_hash[:Login].to_s + table.rows_hash[:login].to_s
user = User.find_by_login(login) unless login.blank?

@ -0,0 +1,37 @@
Feature: User Status
Background:
Given I am already logged in as "admin"
Given there is a user named "bobby"
Scenario: Existing user can be assigned a random password
When I assign the user "bobby" a random password
Then an e-mail should be sent containing "Password"
When I try to log in with user "bobby"
Then I should not see "Bob Bobbit"
When I try to log in with user "bobby" and the password sent via email
Then there should be a flash error message
And there should be a "New password" field
@javascript
Scenario: New user can be assigned a random password
When I create a new user
And I check the assign random password to user field
And I save the new user
Then an e-mail should be sent containing "Password"
When I try to log in with user "newbobby"
Then I should not see "Bob Bobbit"
When I try to log in with user "newbobby" and the password sent via email
Then there should be a flash error message
And there should be a "New password" field
@javascript
Scenario: Password fields are disabled and cleared when random password assignment is activated
When I edit the user "bobby"
And I check the assign random password to user field
Then the password and confirmation fields should be empty
And the password and confirmation fields should be disabled
And the force password change field should be checked
And the force password change field should be disabled
When I click "Save"
When I try to log in with user "bobby"
Then I should not see "Bob Bobbit"

@ -154,6 +154,7 @@ describe User do
it { @u.password.should_not be_blank }
it { @u.password_confirmation.should_not be_blank }
it { @u.force_password_change.should be_true}
end
describe '.system' do

@ -222,7 +222,11 @@ class MailHandlerTest < ActiveSupport::TestCase
assert email.subject.include?('account activation')
login = email.body.encoded.match(/\* Login: (\S+)\s?$/)[1]
password = email.body.encoded.match(/\* Password: (\S+)\s?$/)[1]
assert_equal issue.author, User.try_to_login(login, password)
# Can't log in here since randomly assigned password must be changed
found_user = User.find_by_login(login)
assert_equal issue.author, found_user
assert found_user.check_password?(password)
end
end

Loading…
Cancel
Save