Commit 313ac55539ab93110d070e17811480092d55d8ce

Authored by Dmitriy Zaporozhets
2 parents cfd15eb4 6838304a

Merge branch 'feature/password_expire' of /home/git/repositories/gitlab/gitlabhq

app/controllers/admin/users_controller.rb
@@ -55,8 +55,14 @@ class Admin::UsersController < Admin::ApplicationController @@ -55,8 +55,14 @@ class Admin::UsersController < Admin::ApplicationController
55 def create 55 def create
56 admin = params[:user].delete("admin") 56 admin = params[:user].delete("admin")
57 57
58 - @admin_user = User.new(params[:user], as: :admin) 58 + opts = {
  59 + force_random_password: true,
  60 + password_expires_at: Time.now
  61 + }
  62 +
  63 + @admin_user = User.new(params[:user].merge(opts), as: :admin)
59 @admin_user.admin = (admin && admin.to_i > 0) 64 @admin_user.admin = (admin && admin.to_i > 0)
  65 + @admin_user.created_by_id = current_user.id
60 66
61 respond_to do |format| 67 respond_to do |format|
62 if @admin_user.save 68 if @admin_user.save
app/controllers/application_controller.rb
1 class ApplicationController < ActionController::Base 1 class ApplicationController < ActionController::Base
2 before_filter :authenticate_user! 2 before_filter :authenticate_user!
3 before_filter :reject_blocked! 3 before_filter :reject_blocked!
  4 + before_filter :check_password_expiration
4 before_filter :set_current_user_for_thread 5 before_filter :set_current_user_for_thread
5 before_filter :add_abilities 6 before_filter :add_abilities
6 before_filter :dev_tools if Rails.env == 'development' 7 before_filter :dev_tools if Rails.env == 'development'
@@ -156,4 +157,10 @@ class ApplicationController &lt; ActionController::Base @@ -156,4 +157,10 @@ class ApplicationController &lt; ActionController::Base
156 gon.gravatar_url = request.ssl? || Gitlab.config.gitlab.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url 157 gon.gravatar_url = request.ssl? || Gitlab.config.gitlab.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
157 gon.relative_url_root = Gitlab.config.gitlab.relative_url_root 158 gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
158 end 159 end
  160 +
  161 + def check_password_expiration
  162 + if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now
  163 + redirect_to new_profile_password_path and return
  164 + end
  165 + end
159 end 166 end
app/controllers/passwords_controller.rb 0 → 100644
@@ -0,0 +1,38 @@ @@ -0,0 +1,38 @@
  1 +class PasswordsController < ApplicationController
  2 + layout 'navless'
  3 +
  4 + skip_before_filter :check_password_expiration
  5 +
  6 + before_filter :set_user
  7 + before_filter :set_title
  8 +
  9 + def new
  10 + end
  11 +
  12 + def create
  13 + new_password = params[:user][:password]
  14 + new_password_confirmation = params[:user][:password_confirmation]
  15 +
  16 + result = @user.update_attributes(
  17 + password: new_password,
  18 + password_confirmation: new_password_confirmation
  19 + )
  20 +
  21 + if result
  22 + @user.update_attributes(password_expires_at: nil)
  23 + redirect_to root_path, notice: 'Password successfully changed'
  24 + else
  25 + render :new
  26 + end
  27 + end
  28 +
  29 + private
  30 +
  31 + def set_user
  32 + @user = current_user
  33 + end
  34 +
  35 + def set_title
  36 + @title = "New password"
  37 + end
  38 +end
app/models/user.rb
@@ -42,8 +42,11 @@ class User &lt; ActiveRecord::Base @@ -42,8 +42,11 @@ class User &lt; ActiveRecord::Base
42 42
43 attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username, 43 attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username,
44 :skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password, 44 :skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password,
45 - :extern_uid, :provider, as: [:default, :admin]  
46 - attr_accessible :projects_limit, :can_create_team, :can_create_group, as: :admin 45 + :extern_uid, :provider, :password_expires_at,
  46 + as: [:default, :admin]
  47 +
  48 + attr_accessible :projects_limit, :can_create_team, :can_create_group,
  49 + as: :admin
47 50
48 attr_accessor :force_random_password 51 attr_accessor :force_random_password
49 52
@@ -364,4 +367,8 @@ class User &lt; ActiveRecord::Base @@ -364,4 +367,8 @@ class User &lt; ActiveRecord::Base
364 def accessible_deploy_keys 367 def accessible_deploy_keys
365 DeployKey.in_projects(self.master_projects).uniq 368 DeployKey.in_projects(self.master_projects).uniq
366 end 369 end
  370 +
  371 + def created_by
  372 + User.find_by_id(created_by_id) if created_by_id
  373 + end
367 end 374 end
app/views/admin/users/_form.html.haml
@@ -24,19 +24,25 @@ @@ -24,19 +24,25 @@
24 = f.text_field :email, required: true, autocomplete: "off" 24 = f.text_field :email, required: true, autocomplete: "off"
25 %span.help-inline * required 25 %span.help-inline * required
26 26
27 - %fieldset  
28 - %legend Password  
29 - .clearfix  
30 - = f.label :password  
31 - .input= f.password_field :password, disabled: f.object.force_random_password  
32 - .clearfix  
33 - = f.label :password_confirmation  
34 - .input= f.password_field :password_confirmation, disabled: f.object.force_random_password  
35 - -if f.object.new_record? 27 + - if @admin_user.new_record?
  28 + %fieldset
  29 + %legend Password
  30 + .clearfix
  31 + = f.label :password
  32 + .input
  33 + %strong
  34 + A temporary password will be generated and sent to user.
  35 + %br
  36 + User will be forced to change it after first sign in
  37 + - else
  38 + %fieldset
  39 + %legend Password
  40 + .clearfix
  41 + = f.label :password
  42 + .input= f.password_field :password, disabled: f.object.force_random_password
36 .clearfix 43 .clearfix
37 - = f.label :force_random_password do  
38 - %span Generate random password  
39 - .input= f.check_box :force_random_password, {}, true, nil 44 + = f.label :password_confirmation
  45 + .input= f.password_field :password_confirmation, disabled: f.object.force_random_password
40 46
41 %fieldset 47 %fieldset
42 %legend Access 48 %legend Access
app/views/admin/users/show.html.haml
  1 +%h3.page_title
  2 + User:
  3 + = @admin_user.name
  4 + - if @admin_user.blocked?
  5 + %span.cred (Blocked)
  6 + - if @admin_user.admin
  7 + %span.cred (Admin)
  8 +
  9 + .pull-right
  10 + = link_to edit_admin_user_path(@admin_user), class: "btn grouped btn-small" do
  11 + %i.icon-edit
  12 + Edit
  13 + - unless @admin_user == current_user
  14 + - if @admin_user.blocked?
  15 + = link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn grouped btn-small success"
  16 + - else
  17 + = link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn grouped btn-small btn-remove"
  18 + = link_to 'Destroy', [:admin, @admin_user], confirm: "USER #{@admin_user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn grouped btn-small btn-remove"
  19 +%hr
  20 +
1 .row 21 .row
2 .span6 22 .span6
3 - %h3.page_title  
4 - = image_tag gravatar_icon(@admin_user.email, 90), class: "avatar s90"  
5 - = @admin_user.name  
6 - - if @admin_user.blocked?  
7 - %span.cred (Blocked)  
8 - - if @admin_user.admin  
9 - %span.cred (Admin)  
10 - .pull-right  
11 - = link_to edit_admin_user_path(@admin_user), class: "btn pull-right" do  
12 - %i.icon-edit  
13 - Edit  
14 - %br  
15 - %small @#{@admin_user.username}  
16 - %br  
17 - %small member since #{@admin_user.created_at.stamp("Nov 12, 2031")}  
18 - .clearfix  
19 - %hr  
20 - %p  
21 - %span.btn.btn-small  
22 - %i.icon-envelope  
23 - = mail_to @admin_user.email  
24 - - unless @admin_user == current_user  
25 - - if @admin_user.blocked?  
26 - = link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small success"  
27 - - else  
28 - = link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove"  
29 - = link_to 'Destroy', [:admin, @admin_user], confirm: "USER #{@admin_user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-small btn-remove" 23 + .ui-box
  24 + %h5.title
  25 + Account:
  26 + .pull-right
  27 + = image_tag gravatar_icon(@admin_user.email, 32), class: "avatar s32"
  28 + %ul.well-list
  29 + %li
  30 + %span.light Name:
  31 + %strong= @admin_user.name
  32 + %li
  33 + %span.light Username:
  34 + %strong
  35 + = @admin_user.username
  36 + %li
  37 + %span.light Email:
  38 + %strong
  39 + = mail_to @admin_user.email
  40 +
  41 + %li
  42 + %span.light Member since:
  43 + %strong
  44 + = @admin_user.created_at.stamp("Nov 12, 2031")
  45 +
  46 + %li
  47 + %span.light Last sign-in at:
  48 + %strong
  49 + = @admin_user.last_sign_in_at.stamp("Nov 12, 2031")
  50 +
  51 + - if @admin_user.ldap_user?
  52 + %li
  53 + %span.light LDAP uid:
  54 + %strong
  55 + = @admin_user.extern_uid
  56 +
  57 + - if @admin_user.created_by
  58 + %li
  59 + %span.light Created by:
  60 + %strong
  61 + = link_to @admin_user.created_by.name, [:admin, @admin_user.created_by]
  62 +
30 %hr 63 %hr
31 %h5 64 %h5
32 Add User to Projects 65 Add User to Projects
@@ -67,11 +100,11 @@ @@ -67,11 +100,11 @@
67 100
68 101
69 .span6 102 .span6
70 - = render 'users/profile', user: @admin_user  
71 .ui-box 103 .ui-box
72 %h5.title Projects (#{@projects.count}) 104 %h5.title Projects (#{@projects.count})
73 %ul.well-list 105 %ul.well-list
74 - @projects.sort_by(&:name_with_namespace).each do |project| 106 - @projects.sort_by(&:name_with_namespace).each do |project|
  107 + - tm = project.team.get_tm(@admin_user.id)
75 %li 108 %li
76 = link_to admin_project_path(project), class: dom_class(project) do 109 = link_to admin_project_path(project), class: dom_class(project) do
77 - if project.namespace 110 - if project.namespace
@@ -79,16 +112,17 @@ @@ -79,16 +112,17 @@
79 \/ 112 \/
80 %strong.well-title 113 %strong.well-title
81 = truncate(project.name, length: 45) 114 = truncate(project.name, length: 45)
82 - %span.pull-right.light  
83 - - if project.owner == @admin_user  
84 - %i.icon-wrench  
85 - - tm = project.team.get_tm(@admin_user.id)  
86 - - if tm  
87 - = tm.project_access_human  
88 - = link_to edit_admin_project_member_path(project, tm.user), class: "btn btn-small" do 115 +
  116 + - if project.owner == @admin_user
  117 + %span.label.label-info owner
  118 +
  119 + - if tm
  120 + .pull-right
  121 + = link_to edit_admin_project_member_path(project, tm.user), class: "btn grouped btn-small" do
89 %i.icon-edit 122 %i.icon-edit
90 - = link_to admin_project_member_path(project, tm.user), confirm: remove_from_project_team_message(project, @admin_user), method: :delete, class: "btn btn-small btn-remove" do 123 + = link_to admin_project_member_path(project, tm.user), confirm: remove_from_project_team_message(project, @admin_user), method: :delete, class: "btn grouped btn-small btn-remove" do
91 %i.icon-remove 124 %i.icon-remove
92 - %p.light  
93 - %i.icon-wrench  
94 - &ndash; user is a project owner 125 +
  126 + .pull-right.light
  127 + = tm.project_access_human
  128 + &nbsp;
app/views/notify/new_user_email.html.haml
@@ -8,13 +8,14 @@ @@ -8,13 +8,14 @@
8 %p 8 %p
9 login.......................................... 9 login..........................................
10 %code= @user['email'] 10 %code= @user['email']
11 -%p  
12 - - unless Gitlab.config.gitlab.signup_enabled 11 +
  12 +- if @user.created_by_id
  13 + %p
13 password.................................. 14 password..................................
14 %code= @password 15 %code= @password
15 16
16 -%p  
17 - Please change your password immediately after login. 17 + %p
  18 + You will be forced to change this password immediately after login.
18 19
19 %p 20 %p
20 = link_to "Click here to login", root_url 21 = link_to "Click here to login", root_url
app/views/notify/new_user_email.text.erb
@@ -3,10 +3,11 @@ Hi &lt;%= @user.name %&gt;! @@ -3,10 +3,11 @@ Hi &lt;%= @user.name %&gt;!
3 The Administrator created an account for you. Now you are a member of company GitLab application. 3 The Administrator created an account for you. Now you are a member of company GitLab application.
4 4
5 login.................. <%= @user.email %> 5 login.................. <%= @user.email %>
6 -<% unless Gitlab.config.gitlab.signup_enabled %> 6 +<% if @user.created_by_id %>
7 password............... <%= @password %> 7 password............... <%= @password %>
  8 +
  9 + You will be forced to change this password immediately after login.
8 <% end %> 10 <% end %>
9 11
10 -Please change your password immediately after login.  
11 12
12 Click here to login: <%= url_for(root_url) %> 13 Click here to login: <%= url_for(root_url) %>
app/views/passwords/new.html.haml 0 → 100644
@@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
  1 += form_for @user, url: profile_password_path, method: :post do |f|
  2 + .light-well.padded
  3 + %p.slead
  4 + Please set new password before proceed.
  5 + %br
  6 + After successful password update you will be redirected to login screen
  7 + -if @user.errors.any?
  8 + .alert.alert-error
  9 + %ul
  10 + - @user.errors.full_messages.each do |msg|
  11 + %li= msg
  12 +
  13 + .clearfix
  14 + = f.label :password
  15 + .input= f.password_field :password, required: true
  16 + .clearfix
  17 + = f.label :password_confirmation
  18 + .input
  19 + = f.password_field :password_confirmation, required: true
  20 + .clearfix
  21 + .input
  22 + = f.submit 'Set new password', class: "btn btn-create"
config/routes.rb
@@ -123,6 +123,7 @@ Gitlab::Application.routes.draw do @@ -123,6 +123,7 @@ Gitlab::Application.routes.draw do
123 end 123 end
124 124
125 resource :notifications 125 resource :notifications
  126 + resource :password
126 end 127 end
127 128
128 resources :keys 129 resources :keys
db/fixtures/production/001_admin.rb
@@ -3,7 +3,8 @@ admin = User.create( @@ -3,7 +3,8 @@ admin = User.create(
3 name: "Administrator", 3 name: "Administrator",
4 username: 'root', 4 username: 'root',
5 password: "5iveL!fe", 5 password: "5iveL!fe",
6 - password_confirmation: "5iveL!fe" 6 + password_confirmation: "5iveL!fe",
  7 + password_expires_at: Time.now
7 ) 8 )
8 9
9 admin.projects_limit = 10000 10 admin.projects_limit = 10000
db/migrate/20130613165816_add_password_expires_at_to_users.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddPasswordExpiresAtToUsers < ActiveRecord::Migration
  2 + def change
  3 + add_column :users, :password_expires_at, :datetime
  4 + end
  5 +end
db/migrate/20130613173246_add_created_by_id_to_user.rb 0 → 100644
@@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
  1 +class AddCreatedByIdToUser < ActiveRecord::Migration
  2 + def change
  3 + add_column :users, :created_by_id, :integer
  4 + end
  5 +end
@@ -11,7 +11,7 @@ @@ -11,7 +11,7 @@
11 # 11 #
12 # It's strongly recommended to check this file into your version control system. 12 # It's strongly recommended to check this file into your version control system.
13 13
14 -ActiveRecord::Schema.define(:version => 20130522141856) do 14 +ActiveRecord::Schema.define(:version => 20130613173246) do
15 15
16 create_table "deploy_keys_projects", :force => true do |t| 16 create_table "deploy_keys_projects", :force => true do |t|
17 t.integer "deploy_key_id", :null => false 17 t.integer "deploy_key_id", :null => false
@@ -292,6 +292,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130522141856) do @@ -292,6 +292,8 @@ ActiveRecord::Schema.define(:version =&gt; 20130522141856) do
292 t.string "state" 292 t.string "state"
293 t.integer "color_scheme_id", :default => 1, :null => false 293 t.integer "color_scheme_id", :default => 1, :null => false
294 t.integer "notification_level", :default => 1, :null => false 294 t.integer "notification_level", :default => 1, :null => false
  295 + t.datetime "password_expires_at"
  296 + t.integer "created_by_id"
295 end 297 end
296 298
297 add_index "users", ["admin"], :name => "index_users_on_admin" 299 add_index "users", ["admin"], :name => "index_users_on_admin"
spec/features/admin/admin_users_spec.rb
@@ -20,13 +20,10 @@ describe &quot;Admin::Users&quot; do @@ -20,13 +20,10 @@ describe &quot;Admin::Users&quot; do
20 20
21 describe "GET /admin/users/new" do 21 describe "GET /admin/users/new" do
22 before do 22 before do
23 - @password = "123ABC"  
24 visit new_admin_user_path 23 visit new_admin_user_path
25 fill_in "user_name", with: "Big Bang" 24 fill_in "user_name", with: "Big Bang"
26 fill_in "user_username", with: "bang" 25 fill_in "user_username", with: "bang"
27 fill_in "user_email", with: "bigbang@mail.com" 26 fill_in "user_email", with: "bigbang@mail.com"
28 - fill_in "user_password", with: @password  
29 - fill_in "user_password_confirmation", with: @password  
30 end 27 end
31 28
32 it "should create new user" do 29 it "should create new user" do
@@ -57,26 +54,13 @@ describe &quot;Admin::Users&quot; do @@ -57,26 +54,13 @@ describe &quot;Admin::Users&quot; do
57 end 54 end
58 55
59 it "should send valid email to user with email & password" do 56 it "should send valid email to user with email & password" do
60 - Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)  
61 User.observers.enable :user_observer do 57 User.observers.enable :user_observer do
62 click_button "Create user" 58 click_button "Create user"
63 user = User.last 59 user = User.last
64 email = ActionMailer::Base.deliveries.last 60 email = ActionMailer::Base.deliveries.last
65 email.subject.should have_content("Account was created") 61 email.subject.should have_content("Account was created")
66 email.text_part.body.should have_content(user.email) 62 email.text_part.body.should have_content(user.email)
67 - email.text_part.body.should have_content(@password)  
68 - end  
69 - end  
70 -  
71 - it "should send valid email to user with email without password when signup is enabled" do  
72 - Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)  
73 - User.observers.enable :user_observer do  
74 - click_button "Create user"  
75 - user = User.last  
76 - email = ActionMailer::Base.deliveries.last  
77 - email.subject.should have_content("Account was created")  
78 - email.text_part.body.should have_content(user.email)  
79 - email.text_part.body.should_not have_content(@password) 63 + email.text_part.body.should have_content('password')
80 end 64 end
81 end 65 end
82 end 66 end