Commit 313ac55539ab93110d070e17811480092d55d8ce
Exists in
master
and in
4 other branches
Merge branch 'feature/password_expire' of /home/git/repositories/gitlab/gitlabhq
Showing
15 changed files
with
199 additions
and
79 deletions
Show diff stats
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 < ActionController::Base | @@ -156,4 +157,10 @@ class ApplicationController < 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 |
@@ -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 < ActiveRecord::Base | @@ -42,8 +42,11 @@ class User < 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 < ActiveRecord::Base | @@ -364,4 +367,8 @@ class User < 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 | - – user is a project owner | 125 | + |
126 | + .pull-right.light | ||
127 | + = tm.project_access_human | ||
128 | + |
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 <%= @user.name %>! | @@ -3,10 +3,11 @@ Hi <%= @user.name %>! | ||
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) %> |
@@ -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
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
db/schema.rb
@@ -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 => 20130522141856) do | @@ -292,6 +292,8 @@ ActiveRecord::Schema.define(:version => 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 "Admin::Users" do | @@ -20,13 +20,10 @@ describe "Admin::Users" 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 "Admin::Users" do | @@ -57,26 +54,13 @@ describe "Admin::Users" 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 |