diff --git a/opentech/apply/activity/views.py b/opentech/apply/activity/views.py
index e2ba1afd72454de8b01ba7cc762b382f59f3305b..3d5c2e483cf5e034234dccd2d01def0864a48259 100644
--- a/opentech/apply/activity/views.py
+++ b/opentech/apply/activity/views.py
@@ -1,5 +1,8 @@
+from django.utils.decorators import method_decorator
 from django.views.generic import CreateView, View
 
+from opentech.apply.users.decorators import staff_required
+
 from .forms import CommentForm
 from .models import Activity, COMMENT
 
@@ -27,6 +30,7 @@ class ActivityContextMixin:
         return super().get_context_data(**extra, **kwargs)
 
 
+@method_decorator(staff_required, name='dispatch')
 class DelegatedViewMixin(View):
     """For use on create views accepting forms from another view"""
     def get_template_names(self):
diff --git a/opentech/apply/funds/views.py b/opentech/apply/funds/views.py
index 1ca6807eafbe79d93d5dd964f1b8faa2cbe8851a..d5d9629bd9f14f86f3abf20b8b95e4f6c749bd74 100644
--- a/opentech/apply/funds/views.py
+++ b/opentech/apply/funds/views.py
@@ -1,5 +1,6 @@
 from django import forms
 from django.template.response import TemplateResponse
+from django.utils.decorators import method_decorator
 from django.views.generic import DetailView, UpdateView
 
 from django_filters.views import FilterView
@@ -12,6 +13,7 @@ from opentech.apply.activity.views import (
     DelegatedViewMixin,
 )
 from opentech.apply.activity.models import Activity
+from opentech.apply.users.decorators import staff_required
 
 from .forms import ProgressSubmissionForm, UpdateSubmissionLeadForm
 from .models import ApplicationSubmission
@@ -19,6 +21,7 @@ from .tables import AdminSubmissionsTable, SubmissionFilter, SubmissionFilterAnd
 from .workflow import SingleStage, DoubleStage
 
 
+@method_decorator(staff_required, name='dispatch')
 class SubmissionListView(AllActivityContextMixin, SingleTableMixin, FilterView):
     template_name = 'funds/submissions.html'
     table_class = AdminSubmissionsTable
@@ -30,6 +33,7 @@ class SubmissionListView(AllActivityContextMixin, SingleTableMixin, FilterView):
         return super().get_context_data(active_filters=active_filters, **kwargs)
 
 
+@method_decorator(staff_required, name='dispatch')
 class SubmissionSearchView(SingleTableMixin, FilterView):
     template_name = 'funds/submissions_search.html'
     table_class = AdminSubmissionsTable
@@ -49,6 +53,7 @@ class SubmissionSearchView(SingleTableMixin, FilterView):
         )
 
 
+@method_decorator(staff_required, name='dispatch')
 class ProgressSubmissionView(DelegatedViewMixin, UpdateView):
     model = ApplicationSubmission
     form_class = ProgressSubmissionForm
@@ -66,6 +71,7 @@ class ProgressSubmissionView(DelegatedViewMixin, UpdateView):
         return response
 
 
+@method_decorator(staff_required, name='dispatch')
 class UpdateLeadView(DelegatedViewMixin, UpdateView):
     model = ApplicationSubmission
     form_class = UpdateSubmissionLeadForm
@@ -84,6 +90,7 @@ class UpdateLeadView(DelegatedViewMixin, UpdateView):
         return response
 
 
+@method_decorator(staff_required, name='dispatch')
 class SubmissionDetailView(ActivityContextMixin, DetailView):
     model = ApplicationSubmission
     form_views = {
diff --git a/opentech/apply/users/decorators.py b/opentech/apply/users/decorators.py
index 1ed11e35dc33bcd48ff59a7c2f76dbb47c3956d3..38874fb6cc621b168fb20cdeab960cb4740a5da5 100644
--- a/opentech/apply/users/decorators.py
+++ b/opentech/apply/users/decorators.py
@@ -1,4 +1,5 @@
 from django.core.exceptions import PermissionDenied
+from django.contrib.auth.decorators import login_required, user_passes_test
 
 from .utils import can_use_oauth_check
 
@@ -8,7 +9,14 @@ def require_oauth_whitelist(view_func):
     def decorated_view(request, *args, **kwargs):
         if can_use_oauth_check(request.user):
             return view_func(request, *args, **kwargs)
+        raise PermissionDenied
+    return decorated_view
+
 
+def is_apply_staff(user):
+    if not user.is_apply_staff:
         raise PermissionDenied
+    return True
 
-    return decorated_view
+
+staff_required = [login_required, user_passes_test(is_apply_staff)]
diff --git a/opentech/apply/users/models.py b/opentech/apply/users/models.py
index f4d22666087e3159ec7d4266bb9513d6b4f591ef..9e5d32e2fa0489055e19a6ecf44116a2cb19fc75 100644
--- a/opentech/apply/users/models.py
+++ b/opentech/apply/users/models.py
@@ -2,6 +2,7 @@ from django.db import models
 from django.contrib.auth.models import AbstractUser, BaseUserManager
 from django.utils.translation import gettext_lazy as _
 
+from .groups import STAFF_GROUP_NAME
 from .utils import send_activation_email
 
 
@@ -69,3 +70,7 @@ class User(AbstractUser):
 
     def get_short_name(self):
         return self.email
+
+    @property
+    def is_apply_staff(self):
+        return self.groups.filter(name=STAFF_GROUP_NAME).exists()