diff --git a/app/models/ability.rb b/app/models/ability.rb
index b72178fa12608f84058cb356ee71d7c798e8ebc4..5beead0b75d8e01afb46ec22d316417351581293 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -15,6 +15,7 @@ class Ability
       when "Group" then group_abilities(user, subject)
       when "Namespace" then namespace_abilities(user, subject)
       when "GroupMember" then group_member_abilities(user, subject)
+      when "ProjectMember" then project_member_abilities(user, subject)
       else []
       end.concat(global_abilities(user))
     end
@@ -316,6 +317,23 @@ class Ability
       rules
     end
 
+    def project_member_abilities(user, subject)
+      rules = []
+      target_user = subject.user
+      project = subject.project
+      can_manage = project_abilities(user, project).include?(:admin_project_member)
+
+      if can_manage && (user != target_user)
+        rules << :update_project_member
+        rules << :destroy_project_member
+      end
+
+      if !project.last_owner?(user) && (can_manage || (user == target_user))
+        rules << :destroy_project_member
+      end
+      rules
+    end
+
     def abilities
       @abilities ||= begin
                        abilities = Six.new
diff --git a/app/models/concerns/has_owners.rb b/app/models/concerns/has_owners.rb
new file mode 100644
index 0000000000000000000000000000000000000000..735b207172123741aa960d9bde4a1e31fc8e6937
--- /dev/null
+++ b/app/models/concerns/has_owners.rb
@@ -0,0 +1,31 @@
+# == Owners concern
+#
+# Contains owners functionality for groups
+#
+module HasOwners
+  extend ActiveSupport::Concern
+
+  def owners
+    @owners ||= my_members.owners.includes(:user).map(&:user)
+  end
+
+  def my_members
+    raise NotImplementedError, "Expected my_members to be defined in #{self.class.name}"
+  end
+
+  def add_owner(user, current_user = nil)
+    add_user(user, Gitlab::Access::OWNER, current_user)
+  end
+
+  def has_owner?(user)
+    owners.include?(user)
+  end
+
+  def has_master?(user)
+    members.masters.where(user_id: user).any?
+  end
+
+  def last_owner?(user)
+    has_owner?(user) && owners.size == 1
+  end
+end
diff --git a/app/models/group.rb b/app/models/group.rb
index 465c22d23ac85ae8305112bf9f637fac3693f336..c9806f6fd6f49084ba568133f3cecc6234407198 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -19,8 +19,10 @@ require 'file_size_validator'
 class Group < Namespace
   include Gitlab::ConfigHelper
   include Referable
+  include HasOwners
 
   has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
+  alias_method :my_members, :group_members
   has_many :users, through: :group_members
 
   validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
@@ -63,10 +65,6 @@ class Group < Namespace
     end
   end
 
-  def owners
-    @owners ||= group_members.owners.includes(:user).map(&:user)
-  end
-
   def add_users(user_ids, access_level, current_user = nil)
     user_ids.each do |user_id|
       Member.add_user(self.group_members, user_id, access_level, current_user)
@@ -93,22 +91,6 @@ class Group < Namespace
     add_user(user, Gitlab::Access::MASTER, current_user)
   end
 
-  def add_owner(user, current_user = nil)
-    add_user(user, Gitlab::Access::OWNER, current_user)
-  end
-
-  def has_owner?(user)
-    owners.include?(user)
-  end
-
-  def has_master?(user)
-    members.masters.where(user_id: user).any?
-  end
-
-  def last_owner?(user)
-    has_owner?(user) && owners.size == 1
-  end
-
   def members
     group_members
   end
diff --git a/app/models/member.rb b/app/models/member.rb
index 4651c8fff3766fb296f1134fc8b5f86bc5996ef7..c565ee6bbcee6ae2c9fafcfe2b6700e7261f0534 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -82,8 +82,7 @@ class Member < ActiveRecord::Base
         member.invite_email = user
       end
 
-      project = members.first.respond_to?(:project)? members.first.project : nil
-      if can_update_member?(current_user, member, project)
+      if can_update_member?(current_user, member)
         member.created_by ||= current_user
         member.access_level = access_level
 
@@ -93,9 +92,10 @@ class Member < ActiveRecord::Base
 
     private
 
-    def can_update_member?(current_user, member, project)
+    def can_update_member?(current_user, member)
       !current_user || current_user.can?(:update_group_member, member) ||
-        (project && current_user.can?(:admin_project_member, project))
+        (member.respond_to?(:project) &&
+          current_user.can?(:update_project_member, member))
     end
   end
 
diff --git a/app/models/project.rb b/app/models/project.rb
index 74b89aad49901a4eb274521b116c5149d125487d..79b7a6457d7dccea0c5475a493f980c1b5ebe90f 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -41,6 +41,7 @@ class Project < ActiveRecord::Base
   include Sortable
   include AfterCommitQueue
   include CaseSensitivity
+  include HasOwners
 
   extend Gitlab::ConfigHelper
   extend Enumerize
@@ -114,6 +115,7 @@ class Project < ActiveRecord::Base
   has_many :hooks,              dependent: :destroy, class_name: 'ProjectHook'
   has_many :protected_branches, dependent: :destroy
   has_many :project_members, dependent: :destroy, as: :source, class_name: 'ProjectMember'
+  alias_method :my_members, :project_members
   has_many :users, through: :project_members
   has_many :deploy_keys_projects, dependent: :destroy
   has_many :deploy_keys, through: :deploy_keys_projects