diff --git a/django/contrib/admin/actions.py b/django/contrib/admin/actions.py
index 2011017..b928a85 100644
--- a/django/contrib/admin/actions.py
+++ b/django/contrib/admin/actions.py
@@ -2,14 +2,85 @@
 Built-in, globally-available admin actions.
 """
 
+from django.contrib.auth import REDIRECT_FIELD_NAME
 from django.core.exceptions import PermissionDenied
 from django.contrib.admin import helpers
 from django.contrib.admin.util import get_deleted_objects, model_ngettext
+from django.contrib.auth.decorators import user_passes_test
 from django.db import router
 from django.template.response import TemplateResponse
 from django.utils.encoding import force_text
 from django.utils.translation import ugettext_lazy, ugettext as _
 
+
+def action_user_passes_test(test_func, login_url=None,
+                                   redirect_field_name=REDIRECT_FIELD_NAME):
+    """
+    Decorator for admin actions that acts like user_passes_test view
+    decorator. It checks user permissions, redirecting to the login-page if
+    necessary. The test_func should be callable that takes the user object
+    and modeladmin object, returning True if user passes.
+
+    """
+    def decorator(action_func):
+        def _wrapped_action(modeladmin, request, queryset):
+            if test_func(request.user, modeladmin):
+                return action_func(modeladmin, request, queryset)
+            else:
+                # Redirect to login page as user_passes_test do.
+                # Pass dummy function, since we already know the user don't
+                    # have required permissions.
+                return user_passes_test(lambda: False, login_url,
+                                        redirect_field_name)
+        _wrapped_action.test_func = test_func
+        return _wrapped_action
+    return decorator
+    
+
+def action_permission_required(perm, login_url=None, raise_exception=False):
+    """
+    Decorator for admin actions that acts like permission_required view
+    decorator. It checks user permissions, redirecting to the login-page if
+    necessary.
+
+    Permissions which can be used for multiple models can be specified
+    in glob pattern.
+
+    Usage::
+
+      # Specific action for given application/model
+      @action_permission_required('poll.can_close_poll')
+      def close_poll_action(modeladmin, request, queryset):
+        ...
+
+      # Shared action for multiple applications/models
+      @action_permission_required('*.delete_*')
+      def delete_selected_action(...):
+        ...
+
+    """
+    def check_perms(user, modeladmin):
+        # First check if the user has the permission (even anon users)
+        if perm.startswith('*'):
+            # Strip '*' from the beginning and from the end
+            partial_perm = ''.join([modeladmin.model._meta.app_label, perm[1:-1]])
+            # If any of user perms match, return True
+            all_perms = user.get_all_permissions()
+            filter_func = lambda p: p.startswith(partial_perm)
+            if any(filter(filter_func, all_perms)):
+                return True
+        else:
+            if user.has_perm(perm):
+                return True
+
+        # In case the 403 handler should be called raise the exception
+        if raise_exception:
+            raise PermissionDenied
+        return False
+    return action_user_passes_test(check_perms, login_url)
+            
+
+@action_permission_required('*.delete_*')
 def delete_selected(modeladmin, request, queryset):
     """
     Default action which deletes the selected objects.
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index f4205f2..2586770 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -594,6 +594,14 @@ class ModelAdmin(BaseModelAdmin):
 
         # get_action might have returned None, so filter any of those out.
         actions = filter(None, actions)
+        
+        # filter actions which user don't have permission to trigger
+        def filter_forbidden(action_tuple):
+            action = action_tuple[0]
+            if hasattr(action, 'test_func'):
+                return action.test_func(request.user, self)
+            return True
+        actions = filter(filter_forbidden, actions)
 
         # Convert the actions into a SortedDict keyed by name.
         actions = SortedDict([
diff --git a/docs/ref/contrib/admin/actions.txt b/docs/ref/contrib/admin/actions.txt
index 3f3b529..9579bfa 100644
--- a/docs/ref/contrib/admin/actions.txt
+++ b/docs/ref/contrib/admin/actions.txt
@@ -348,4 +348,25 @@ Conditionally enabling or disabling actions
                         del actions['delete_selected']
                 return actions
 
+Enabling admin actions for specific user permissions only
+---------------------------------------------------------
 
+.. versionadded:: 1.5
+
+Admin actions can be displayed depending on user permissions using
+:func:`~action_permissions_required` decorator
+from ``django.contrib.admin.actions`` module.
+
+.. function:: action_permissions_required(permission, [redirect_field_name=REDIRECT_FIELD_NAME, login_url=None])
+
+   *permission* is required permission, eg. polls.can_vote.
+   
+   If you want to add shared action which is visible site-wide, use glob
+   pattern, \*.delete\_\*. Other variants (\*.detete_entry or poll.add\_\*)
+   are not valid.
+
+.. function:: action_user_passes_test(test_func, [redirect_field_name=REDIRECT_FIELD_NAME, login_url=None])
+
+   This is more general version of :func:`action_permissions_required`, where
+   *test_func* function accepts user and modeladmin instances. It returns
+   True, when action should be available and False otherwise.
diff --git a/tests/regressiontests/admin_views/admin.py b/tests/regressiontests/admin_views/admin.py
index a5476e9..53f2314 100644
--- a/tests/regressiontests/admin_views/admin.py
+++ b/tests/regressiontests/admin_views/admin.py
@@ -6,6 +6,7 @@ import os
 
 from django import forms
 from django.contrib import admin
+from django.contrib.admin.actions import action_permission_required
 from django.contrib.admin.views.main import ChangeList
 from django.core.files.storage import FileSystemStorage
 from django.core.mail import EmailMessage
@@ -27,7 +28,7 @@ from .models import (Article, Chapter, Account, Media, Child, Parent, Picture,
     Album, Question, Answer, ComplexSortedPerson, PrePopulatedPostLargeSlug,
     AdminOrderedField, AdminOrderedModelMethod, AdminOrderedAdminMethod,
     AdminOrderedCallable, Report, Color2, UnorderedObject, MainPrepopulated,
-    RelatedPrepopulated, UndeletableObject, Simple)
+    RelatedPrepopulated, UndeletableObject, Simple, RestrictedActionsModel)
 
 
 def callable_year(dt_value):
@@ -586,6 +587,22 @@ class AttributeErrorRaisingAdmin(admin.ModelAdmin):
     list_display = [callable_on_unknown, ]
 
 
+class RestrictedActionsAdmin(admin.ModelAdmin):
+    actions = ['public_action', 'shared_delete_action', 'specific_add_action']
+
+    def public_action(modeladmin, request, queryset):
+        return HttpResponse('Public action!')
+
+    @action_permission_required('*.delete_*')
+    def shared_delete_action(modeladmin, request, queryset):
+        return HttpResponse('Shared delete action!')
+
+    @action_permission_required('admin_views.add_restrictedactionsmodel')
+    def specific_add_action(modeladmin, request, queryset):
+        return HttpResponse('Specific add action!')
+        
+
+
 site = admin.AdminSite(name="admin")
 site.register(Article, ArticleAdmin)
 site.register(CustomArticle, CustomArticleAdmin)
@@ -660,6 +677,7 @@ site.register(AdminOrderedAdminMethod, AdminOrderedAdminMethodAdmin)
 site.register(AdminOrderedCallable, AdminOrderedCallableAdmin)
 site.register(Color2, CustomTemplateFilterColorAdmin)
 site.register(Simple, AttributeErrorRaisingAdmin)
+site.register(RestrictedActionsModel, RestrictedActionsAdmin)
 
 # Register core models we need in our tests
 from django.contrib.auth.models import User, Group
diff --git a/tests/regressiontests/admin_views/fixtures/admin-users-permissions.xml b/tests/regressiontests/admin_views/fixtures/admin-users-permissions.xml
new file mode 100644
index 0000000..0a8e8ff
--- /dev/null
+++ b/tests/regressiontests/admin_views/fixtures/admin-users-permissions.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<django-objects version="1.0">
+    <object pk="141" model="auth.user">
+        <field type="CharField" name="username">anotheradduser</field>
+        <field type="CharField" name="first_name">Add</field>
+        <field type="CharField" name="last_name">User</field>
+        <field type="CharField" name="email">auser@example.com</field>
+        <field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
+        <field type="BooleanField" name="is_staff">True</field>
+        <field type="BooleanField" name="is_active">True</field>
+        <field type="BooleanField" name="is_superuser">False</field>
+        <field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
+        <field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
+        <field to="auth.group" name="groups" rel="ManyToManyRel"></field>
+        <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
+    </object>
+    <object pk="142" model="auth.user">
+        <field type="CharField" name="username">anotherdeleteuser</field>
+        <field type="CharField" name="first_name">Delete</field>
+        <field type="CharField" name="last_name">User</field>
+        <field type="CharField" name="email">duser@example.com</field>
+        <field type="CharField" name="password">sha1$995a3$6011485ea3834267d719b4c801409b8b1ddd0158</field>
+        <field type="BooleanField" name="is_staff">True</field>
+        <field type="BooleanField" name="is_active">True</field>
+        <field type="BooleanField" name="is_superuser">False</field>
+        <field type="DateTimeField" name="last_login">2007-05-30 13:20:10</field>
+        <field type="DateTimeField" name="date_joined">2007-05-30 13:20:10</field>
+        <field to="auth.group" name="groups" rel="ManyToManyRel"></field>
+        <field to="auth.permission" name="user_permissions" rel="ManyToManyRel"></field>
+    </object>
+    <object pk="142" model="contenttypes.contenttype">
+        <field type="CharField" name="name">anothermodel</field>
+        <field type="CharField" name="app_label">admin_views</field>
+        <field type="CharField" name="model">anothermodel</field>
+    </object>
+    <object pk="141" model="auth.permission">
+        <field type="CharField" name="name">Can add AnotherModel</field>
+        <field type="CharField" name="codename">add_anothermodel</field>
+        <field to="contenttypes.contenttype" rel="ForeignKey" name="content_type">142</field>
+    </object>
+    <object pk="142" model="auth.permission">
+        <field type="CharField" name="name">Can delete AnotherModel</field>
+        <field type="CharField" name="codename">delete_anothermodel</field>
+        <field to="contenttypes.contenttype" rel="ForeignKey" name="content_type">142</field>
+    </object>
+    <object pk="142" model="auth.user_user_permissions">
+        <field to="auth.user" rel="ForeignKey" name="user">101</field>
+        <field to="auth.permissions" rel="ForeignKey" name="permission">
+            <natural>add_restrictedactionsmodel</natural>
+            <natural>admin_views</natural>
+            <natural>restrictedactionsmodel</natural>
+        </field>
+    </object>
+    <object pk="143" model="auth.user_user_permissions">
+        <field to="auth.user" rel="ForeignKey" name="user">103</field>
+        <field to="auth.permissions" rel="ForeignKey" name="permission">
+            <natural>delete_restrictedactionsmodel</natural>
+            <natural>admin_views</natural>
+            <natural>restrictedactionsmodel</natural>
+        </field>
+    </object>
+    <object pk="144" model="auth.user_user_permissions">
+        <field to="auth.user" rel="ForeignKey" name="user">141</field>
+        <field to="auth.permissions" rel="ForeignKey" name="permission">141</field>
+    </object>
+    <object pk="145" model="auth.user_user_permissions">
+        <field to="auth.user" rel="ForeignKey" name="user">142</field>
+        <field to="auth.permissions" rel="ForeignKey" name="permission">142</field>
+    </object>
+</django-objects>
diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py
index 2b14300..8a70a64 100644
--- a/tests/regressiontests/admin_views/models.py
+++ b/tests/regressiontests/admin_views/models.py
@@ -656,3 +656,9 @@ class Simple(models.Model):
     """
     Simple model with nothing on it for use in testing
     """
+
+
+class RestrictedActionsModel(models.Model):
+    """
+    Just another simple model to use with RestrictedActionsAdmin.
+    """
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index 72dc6a3..d9d4e66 100644
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -28,6 +28,7 @@ from django.contrib.contenttypes.models import ContentType
 from django.forms.util import ErrorList
 from django.template.response import TemplateResponse
 from django.test import TestCase
+from django.test.client import RequestFactory
 from django.utils import formats, translation, unittest
 from django.utils.cache import get_max_age
 from django.utils.encoding import iri_to_uri, force_bytes
@@ -46,7 +47,9 @@ from .models import (Article, BarAccount, CustomArticle, EmptyModel, FooAccount,
     OtherStory, ComplexSortedPerson, Parent, Child, AdminOrderedField,
     AdminOrderedModelMethod, AdminOrderedAdminMethod, AdminOrderedCallable,
     Report, MainPrepopulated, RelatedPrepopulated, UnorderedObject,
-    Simple, UndeletableObject)
+    Simple, UndeletableObject, RestrictedActionsModel)
+
+from .admin import site
 
 
 ERROR_MESSAGE = "Please enter the correct username and password \
@@ -3693,3 +3696,58 @@ class AdminViewLogoutTest(TestCase):
         self.assertEqual(response.template_name, 'admin/login.html')
         self.assertEqual(response.request['PATH_INFO'], '/test_admin/admin/')
         self.assertContains(response, '<input type="hidden" name="next" value="/test_admin/admin/" />')
+
+class AdminActionPermissionsTest(TestCase):
+    """ Test permissions for admin action.
+
+    Users:
+    - adduser - has admin_views.add_restrictedactionsmodel permission
+    - deleteuser - has admin_views.delete_restrictedactionsmodel permission
+    - addanotheruser - has admin_views.add_anothermodel permission
+    - anotherdeleteuser - has admin_views.delete_anothermodel permission
+
+    Actions:
+    - public action - visible for everyone (default)
+    - shared_delete_action - visible for any delete_* permission
+    - specific_add_action - visible for admin_views.add_restrictedactionsmodel
+
+    """
+    #urls = "regressiontests.admin_views.urls"
+    fixtures = ['admin-views-users.xml', 'admin-users-permissions.xml']
+
+    def setUp(self):
+        self.factory = RequestFactory()
+        self.modeladmin = site._registry[RestrictedActionsModel]
+        self.dummy_request = self.factory.get('/')
+
+    def test_get_actions_as_superuser(self):
+        """
+        Superuser should see all actions.
+        """
+        request = self.dummy_request
+
+        request.user = User.objects.get(username='super')
+        actions = ['delete_selected', 'public_action', 'shared_delete_action',
+                   'specific_add_action']
+        self.assertEqual(actions, self.modeladmin.get_actions(request).keys())
+
+    def test_shared_actions(self):
+        request = self.dummy_request
+
+        request.user = User.objects.get(username='deleteuser')
+        actions = ['delete_selected', 'public_action', 'shared_delete_action']
+        self.assertEqual(actions, self.modeladmin.get_actions(request).keys())
+
+        request.user = User.objects.get(username='anotherdeleteuser')
+        self.assertEqual(actions, self.modeladmin.get_actions(request).keys())
+
+    def test_specific_actions(self):
+        request = self.dummy_request
+
+        request.user = User.objects.get(username='adduser')
+        actions = ['public_action', 'specific_add_action']
+        self.assertEqual(actions, self.modeladmin.get_actions(request).keys())
+
+        request.user = User.objects.get(username='anotheradduser')
+        actions = ['public_action']
+        self.assertEqual(actions, self.modeladmin.get_actions(request).keys())
