Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--app/controllers/admin/users_controller.rb8
-rw-r--r--config/locales/en.rb5
-rw-r--r--lib/gitorious/user_administration.rb114
-rwxr-xr-xscript/create_user21
-rwxr-xr-xscript/suspend_user40
-rw-r--r--test/unit/lib/gitorious/user_administration_test.rb110
7 files changed, 297 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index 8894255..eb5b11d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,4 @@ public/stylesheets/sites/*
public/javascripts/sites/*
public/system/avatars
public/system/group_avatars
+/.keeptesting
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index c49a665..725a6cb 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -19,6 +19,8 @@
#++
class Admin::UsersController < AdminController
+ include Gitorious::UserAdministration
+
def index
@users = paginate(:action => "index") do
User.paginate(:all, :order => 'suspended_at, login', :page => params[:page])
@@ -58,12 +60,14 @@ class Admin::UsersController < AdminController
def suspend
@user = User.find_by_login!(params[:id])
- @user.suspend
+ suspend_summary = suspend_user(@user)
+
if @user.save
- flash[:notice] = I18n.t("admin.users_controller.suspend_notice", :user_name => @user.login)
+ flash[:notice] = suspend_summary
else
flash[:error] = I18n.t("admin.users_controller.suspend_error", :user_name => @user.login)
end
+
redirect_to admin_users_url
end
diff --git a/config/locales/en.rb b/config/locales/en.rb
index 5f1d155..c8628d3 100644
--- a/config/locales/en.rb
+++ b/config/locales/en.rb
@@ -14,6 +14,11 @@
:unsuspend_notice => "User {{user_name}} was successfully unsuspended.",
:unsuspend_error => "Unable to unsuspend user {{user_name}}."
},
+ :user_suspend => {
+ :suspended_cannot_log_back_in_or_run_git_ops => "Suspended Gitorious account. User will not be able to log back in. Ssh keys are revoked. User can no longer push code to the site.",
+ :removed_user_from_teams => "Removed user from team(s): {{group_names}}",
+ :removed_user_committerships_from_repos => "Removed user committerships from repo(s): {{repo_names}}"
+ },
:check_admin => "For Administrators Only"
},
:mailer => {
diff --git a/lib/gitorious/user_administration.rb b/lib/gitorious/user_administration.rb
new file mode 100644
index 0000000..2e1d430
--- /dev/null
+++ b/lib/gitorious/user_administration.rb
@@ -0,0 +1,114 @@
+
+# encoding: utf-8
+#--
+# Copyright (C) 2012 Gitorious AS
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#++
+
+
+module Gitorious
+ module UserAdministration
+
+ def suspend_user(user)
+ summarized do |s|
+ s << suspend_account(user)
+
+ if(user.groups.count > 0)
+ s << remove_from_teams(user)
+ end
+
+ if(committership_count(user) > 0)
+ s << remove_committerships(user)
+ end
+ end
+ end
+
+ protected
+
+ def committership_count(user)
+ Committership.all(:conditions => {:committer_id => user.id, :committer_type => "User"}).count
+ end
+
+ def suspend_account(user)
+ summarized do |s|
+ user.suspend
+ user.save
+ s << " "+I18n.t("admin.user_suspend.suspended_cannot_log_back_in_or_run_git_ops")
+ end
+ end
+
+ def remove_from_teams(user)
+ summarized do |s|
+ groups = user.groups
+ groups.each do |g|
+ g.members.delete(user)
+ g.save
+ end
+ group_names = groups.map { |g| g.name }.join(", ")
+ s << " "+I18n.t("admin.user_suspend.removed_user_from_teams", :group_names => group_names)
+ end
+ end
+
+ def remove_committerships(user)
+ summarized do |s|
+ committerships = Committership.all(:conditions => {:committer_id => user.id, :committer_type => "User"})
+
+ repos = []
+ committerships.each do |c|
+ if c.repository
+ repos << c.repository
+ end
+ c.delete
+ end
+
+ repo_names = repos.uniq.map { |r| r.name }.join(", ")
+ s << " "+I18n.t("admin.user_suspend.removed_user_committerships_from_repos", :repo_names => repo_names)
+ end
+ end
+
+ def teams_orphaned_by_user_leaving(user)
+ member_groups = user.groups
+ user.groups.find_all{ |group| sole_admin?(user, group)}
+ end
+
+ def sole_admin?(user, group)
+ admins = group.memberships.select{|m| m.role.name == "Administrator"}
+ admins.none? {|a| a.user != user}
+ end
+
+ def projects_orphaned_by_user_leaving(user)
+ orphans = Project.all(:conditions => {:owner_id => user.id, :owner_type => "User"})
+ end
+
+ private
+
+ def summarized
+ summary = ""
+ yield(summary)
+ return summary
+ end
+
+ end
+end
+
+
+
+
+
+
+
+
+
+
diff --git a/script/create_user b/script/create_user
new file mode 100755
index 0000000..4d6ed8f
--- /dev/null
+++ b/script/create_user
@@ -0,0 +1,21 @@
+#!/usr/bin/env ruby
+require File.dirname(__FILE__)+'/../config/environment'
+ActionMailer::Base.raise_delivery_errors = false
+ActionMailer::Base.delivery_method = :test
+
+puts "Type in users login: "
+login = gets.strip
+
+puts "Type in users e-mail: "
+email = gets.strip
+puts "Type in users password: "
+password = gets.strip
+
+user = User.new(:email => email, :terms_of_use => '1')
+user.password = password
+user.password_confirmation = password
+user.login = login
+
+user.save!
+user.activate
+puts "User '#{login}' created successfully."
diff --git a/script/suspend_user b/script/suspend_user
new file mode 100755
index 0000000..e3c399f
--- /dev/null
+++ b/script/suspend_user
@@ -0,0 +1,40 @@
+#!/usr/bin/env ruby
+
+def print_usage
+ puts ""
+ puts "Usage: suspend_user USER_EMAIL_ADDRESS"
+ puts ""
+ puts "Suspends specified Gitorious user, revoking all access to web UI and git operations."
+ puts ""
+ puts "Account is suspended, can no longer log in and loses current browser session."
+ puts "SSH keys are revoked, user no longer able to push, pull, clone git repositories"
+ puts "Committerships and team memberships are removed."
+ puts ""
+end
+
+puts "---Loading Gitorious environment---"
+require File.dirname(__FILE__)+'/../config/environment'
+puts "---Done loading Gitorious environment---\n\n"
+
+def find_user(user_email)
+ user = User.find_by_email(user_email)
+ if !user
+ puts "No Gitorious account with email '#{user_email}', exiting..."
+ exit 1
+ end
+ return user
+end
+
+include Gitorious::UserAdministration
+
+if !ARGV[0]
+ print_usage
+ exit 1
+end
+
+user_email = ARGV[0]
+
+puts "Suspending '#{user_email}'..."
+
+user = find_user(user_email)
+puts suspend_user(user)
diff --git a/test/unit/lib/gitorious/user_administration_test.rb b/test/unit/lib/gitorious/user_administration_test.rb
new file mode 100644
index 0000000..4235e8a
--- /dev/null
+++ b/test/unit/lib/gitorious/user_administration_test.rb
@@ -0,0 +1,110 @@
+# encoding: utf-8
+#--
+# Copyright (C) 2012 Gitorious
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#++
+
+require 'test_helper'
+
+class UserAdministrationTest < ActiveSupport::TestCase
+ include Gitorious::UserAdministration
+
+ should "summarize operations for feedback in CLI or GUI" do
+ summary = suspend_user(User.new)
+ assert summary.length > 0
+ assert summary.class == String
+ assert summary =~ /Suspended Gitorious account/
+ end
+
+ context "team cleanup" do
+ setup do
+ @g = groups(:team_thunderbird) # Already has Mike
+ @user = users(:johan)
+ @g.add_member(@user, Role.admin)
+ end
+
+ should "remove user from all his current teams" do
+ assert_equal 2, @g.members.size
+ remove_from_teams(@user)
+ @g.reload
+ assert_equal 1, @g.members.size
+ end
+
+ should "report which teams user has been removed from" do
+ summary = remove_from_teams(@user)
+ assert summary =~ /Removed user from team\(s\): team-thunderbird, a-team/
+ end
+ end
+
+ context "committership cleanup" do
+ setup do
+ @r1 = repositories(:johans)
+ @r2 = repositories(:moes)
+ @user = users(:mike)
+
+ c = @r1.committerships.new
+ c.creator = @user
+ c.committer = @user
+ c.build_permissions :review, :admin, :commit
+ c.save
+ end
+
+ should "remove the users committerships" do
+ assert_equal 2, @r1.committerships.size
+ assert_equal 1, @r2.committerships.size
+ remove_committerships(@user)
+ assert_equal 1, @r1.committerships.size
+ assert_equal 1, @r2.committerships.size
+ end
+
+ should "report repos where committerships were deleted" do
+ summary = remove_committerships(@user)
+ assert summary =~ /Removed user committerships from repo\(s\): johansprojectrepos/
+ end
+ end
+
+ context "reporting on orphaned teams" do
+ setup do
+ @user = users(:johan)
+ @g1 = groups(:a_team) # Johan is sole admin in this one
+ @g2 = groups(:team_thunderbird) # Mike is admin as well in this one
+ @g2.add_member(@user, Role.admin)
+ end
+
+ should "build list of teams which are orphaned if user leaves" do
+ orphans = teams_orphaned_by_user_leaving(@user)
+ assert sole_admin?(users(:johan), @g1)
+ assert !sole_admin?(users(:johan), @g2)
+ assert_equal 1, orphans.size
+ assert orphans.include?(@g1)
+ assert !orphans.include?(@g2)
+ end
+ end
+
+ context "reporting on orphaned projects" do
+ setup do
+ @user = users(:johan)
+ @johans_project = projects(:johans)
+ @johans_project.owner = @user
+ @johans_project.save
+ @other_project = projects(:johans)
+ end
+
+ should "build list of projects which are orphaned if user leaves" do
+ orphans = projects_orphaned_by_user_leaving(@user)
+ assert orphans.all?{|p| p.owner.id == @user.id}
+ end
+ end
+end