diff --git a/app/controllers/projects/protected_tags/application_controller.rb b/app/controllers/projects/protected_tags/application_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..524b1c62d5585868e0ec2b5b3172059cf143af13
--- /dev/null
+++ b/app/controllers/projects/protected_tags/application_controller.rb
@@ -0,0 +1,7 @@
+class Projects::ProtectedTags::ApplicationController < Projects::ApplicationController
+  protected
+
+  def load_protected_tag
+    @protected_tag = @project.protected_tags.find(params[:protected_tag_id])
+  end
+end
diff --git a/app/controllers/projects/protected_tags/create_access_levels_controller.rb b/app/controllers/projects/protected_tags/create_access_levels_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f7d3bab09bc2aa9d3bf268a0b7bb41b82129441c
--- /dev/null
+++ b/app/controllers/projects/protected_tags/create_access_levels_controller.rb
@@ -0,0 +1,15 @@
+module Projects
+  module ProtectedTags
+    class CreateAccessLevelsController < ProtectedTags::ApplicationController
+      before_action :load_protected_tag, only: [:destroy]
+
+      def destroy
+        @create_access_level = @protected_tag.create_access_levels.find(params[:id])
+        @create_access_level.destroy
+
+        redirect_to namespace_project_protected_tag_path(@project.namespace, @project, @protected_tag),
+                    notice: "Successfully deleted. #{@create_access_level.humanize} will not be able to create this protected tag."
+      end
+    end
+  end
+end
diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb
index 5b75b51148956d83ca37cd0ad9709fea402604d4..fdc8a89ce6cc17ab69481a5d45ee7a65328e5e05 100644
--- a/app/controllers/projects/settings/repository_controller.rb
+++ b/app/controllers/projects/settings/repository_controller.rb
@@ -31,6 +31,7 @@ module Projects
         {
           selected_merge_access_levels: @protected_branch.merge_access_levels.map { |access_level| access_level.user_id || access_level.access_level },
           selected_push_access_levels: @protected_branch.push_access_levels.map { |access_level| access_level.user_id || access_level.access_level },
+          selected_create_access_levels: @protected_tag.create_access_levels.map { |access_level| access_level.user_id || access_level.access_level },
           create_access_levels: levels_for_dropdown(ProtectedTag::CreateAccessLevel),
           push_access_levels: levels_for_dropdown(ProtectedBranch::PushAccessLevel),
           merge_access_levels: levels_for_dropdown(ProtectedBranch::MergeAccessLevel)
diff --git a/app/models/concerns/protected_ref.rb b/app/models/concerns/protected_ref.rb
index 62eaec2407f50fb020614715ecfae639ae8a8dbc..0572d399409e29ddc6c4ad1acc02998cb312e3c9 100644
--- a/app/models/concerns/protected_ref.rb
+++ b/app/models/concerns/protected_ref.rb
@@ -9,6 +9,21 @@ module ProtectedRef
 
     delegate :matching, :matches?, :wildcard?, to: :ref_matcher
 
+    def self.protected_ref_access_levels(*types)
+      types.each do |type|
+        has_many :"#{type}_access_levels", dependent: :destroy
+
+        validates :"#{type}_access_levels", length: { minimum: 0 }
+
+        accepts_nested_attributes_for :"#{type}_access_levels", allow_destroy: true
+
+        # Returns access levels that grant the specified access type to the given user / group.
+        access_level_class = const_get("#{type}_access_level".camelize)
+        scope :"#{type}_access_by_user", -> (user) { access_level_class.joins(:protected_branch).where(protected_branch_id: self.ids).merge(access_level_class.by_user(user)) }
+        scope :"#{type}_access_by_group", -> (group) { access_level_class.joins(:protected_branch).where(protected_branch_id: self.ids).merge(access_level_class.by_group(group)) }
+      end
+    end
+
     def self.protected_ref_accessible_to?(ref, user, action:)
       access_levels_for_ref(ref, action: action).any? do |access_level|
         access_level.check_access(user)
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 9079f4a17e9f27dcb49d35bd2181d138cb219082..ea0a06ad6f93a77bfda65fd302312fcb3e200a54 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -2,30 +2,7 @@ class ProtectedBranch < ActiveRecord::Base
   include Gitlab::ShellAdapter
   include ProtectedRef
 
-  has_many :merge_access_levels, dependent: :destroy
-  has_many :push_access_levels, dependent: :destroy
-
-  validates :merge_access_levels, length: { minimum: 0 }
-  validates :push_access_levels, length: { minimum: 0 }
-
-  accepts_nested_attributes_for :push_access_levels, allow_destroy: true
-  accepts_nested_attributes_for :merge_access_levels, allow_destroy: true
-
-  # Returns all merge access levels (for protected branches in scope) that grant merge
-  # access to the given user.
-  scope :merge_access_by_user, -> (user) { MergeAccessLevel.joins(:protected_branch).where(protected_branch_id: self.ids).merge(MergeAccessLevel.by_user(user)) }
-
-  # Returns all push access levels (for protected branches in scope) that grant push
-  # access to the given user.
-  scope :push_access_by_user, -> (user) { PushAccessLevel.joins(:protected_branch).where(protected_branch_id: self.ids).merge(PushAccessLevel.by_user(user)) }
-
-  # Returns all merge access levels (for protected branches in scope) that grant merge
-  # access to the given group.
-  scope :merge_access_by_group, -> (group) { MergeAccessLevel.joins(:protected_branch).where(protected_branch_id: self.ids).merge(MergeAccessLevel.by_group(group)) }
-
-  # Returns all push access levels (for protected branches in scope) that grant push
-  # access to the given group.
-  scope :push_access_by_group, -> (group) { PushAccessLevel.joins(:protected_branch).where(protected_branch_id: self.ids).merge(PushAccessLevel.by_group(group)) }
+  protected_ref_access_levels :merge, :push
 
   # Returns a hash were keys are types of push access levels (user, role), and
   # values are the number of access levels of the particular type.
diff --git a/app/models/protected_tag.rb b/app/models/protected_tag.rb
index 839640955162101d2bbca681d38a1c0a65b557d8..f38109c0e5275bdf55f7da13f1d4368f6cab0697 100644
--- a/app/models/protected_tag.rb
+++ b/app/models/protected_tag.rb
@@ -2,11 +2,7 @@ class ProtectedTag < ActiveRecord::Base
   include Gitlab::ShellAdapter
   include ProtectedRef
 
-  has_many :create_access_levels, dependent: :destroy
-
-  validates :create_access_levels, length: { is: 1, message: "are restricted to a single instance per protected tag." }
-
-  accepts_nested_attributes_for :create_access_levels
+  protected_ref_access_levels :create
 
   def self.protected?(project, ref_name)
     self.matching(ref_name, protected_refs: project.protected_tags).present?