diff --git a/aleksis/core/migrations/0039_personal_ical_url.py b/aleksis/core/migrations/0039_personal_ical_url.py
index e9526cebbfbf6332a251765877227847eff24f4d..02d9cb3a9e3589563c7dd68ce211271264c5245e 100644
--- a/aleksis/core/migrations/0039_personal_ical_url.py
+++ b/aleksis/core/migrations/0039_personal_ical_url.py
@@ -28,14 +28,4 @@ class Migration(migrations.Migration):
                 'verbose_name_plural': 'Personal Calendar URLs',
             },
         ),
-        migrations.AlterModelOptions(
-            name='globalpermissions',
-            options={'default_permissions': (), 'managed': False, 'permissions': (
-            ('view_system_status', 'Can view system status'), ('manage_data', 'Can manage data'),
-            ('impersonate', 'Can impersonate'), ('search', 'Can use search'),
-            ('change_site_preferences', 'Can change site preferences'),
-            ('change_person_preferences', 'Can change person preferences'),
-            ('change_group_preferences', 'Can change group preferences'), ('test_pdf', 'Can test PDF generation'),
-            ('invite', 'Can invite persons'), ('use_ical', 'Can create and consume iCal feeds'))},
-        ),
     ]
diff --git a/aleksis/core/migrations/0040_add_ical_url_global_permission.py b/aleksis/core/migrations/0040_add_ical_url_global_permission.py
deleted file mode 100644
index f3d00404b40e2f6b086eaf31f6cb37e6e295e6e4..0000000000000000000000000000000000000000
--- a/aleksis/core/migrations/0040_add_ical_url_global_permission.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 3.2.12 on 2022-03-01 09:51
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
-    dependencies = [
-        ('core', '0039_personal_ical_url'),
-    ]
-
-    operations = [
-        migrations.AlterModelOptions(
-            name='globalpermissions',
-            options={'default_permissions': (), 'managed': False, 'permissions': (('view_system_status', 'Can view system status'), ('manage_data', 'Can manage data'), ('impersonate', 'Can impersonate'), ('search', 'Can use search'), ('change_site_preferences', 'Can change site preferences'), ('change_person_preferences', 'Can change person preferences'), ('change_group_preferences', 'Can change group preferences'), ('test_pdf', 'Can test PDF generation'), ('invite', 'Can invite persons'), ('use_ical', 'Can create and consume iCal feeds'))},
-        ),
-    ]
diff --git a/aleksis/core/models.py b/aleksis/core/models.py
index 37061508434ebbd274c97c8af55c55c8360395e4..e310c7baaae51d00f6e04e9d426fc5c7203615c0 100644
--- a/aleksis/core/models.py
+++ b/aleksis/core/models.py
@@ -1120,7 +1120,6 @@ class GlobalPermissions(GlobalPermissionModel):
             ("change_group_preferences", _("Can change group preferences")),
             ("test_pdf", _("Can test PDF generation")),
             ("invite", _("Can invite persons")),
-            ("use_ical", _("Can create and consume iCal feeds")),
         )
 
 
diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py
index b43c43003c933d8797e069e26f6daebc884c7d09..a6b478f26144b9fdea74804dccdb7a1913b01c30 100644
--- a/aleksis/core/rules.py
+++ b/aleksis/core/rules.py
@@ -7,6 +7,7 @@ from .util.predicates import (
     has_global_perm,
     has_object_perm,
     has_person,
+    is_assigned_to_current_person,
     is_current_person,
     is_group_owner,
     is_notification_recipient,
@@ -358,5 +359,15 @@ rules.add_perm("core.manage_permissions", manage_person_permissions_predicate)
 test_pdf_generation_predicate = has_person & has_global_perm("core.test_pdf")
 rules.add_perm("core.test_pdf_rule", test_pdf_generation_predicate)
 
-use_ical_predicate = has_person & has_global_perm("core.use_ical")
-rules.add_perm("core.use_ical_rule", use_ical_predicate)
+# Do CRUD on PersonalICalUrls
+view_ical_predicate = has_person
+rules.add_perm("core.view_ical_rule", view_ical_predicate)
+
+create_ical_predicate = view_ical_predicate
+rules.add_perm("core.create_ical_rule", create_ical_predicate)
+
+edit_ical_predicate = view_ical_predicate & is_assigned_to_current_person
+rules.add_perm("core.edit_ical_rule", edit_ical_predicate)
+
+delete_ical_predicate = edit_ical_predicate
+rules.add_perm("core.delete_ical_rule", delete_ical_predicate)
diff --git a/aleksis/core/util/predicates.py b/aleksis/core/util/predicates.py
index bcba7e8637d81aa9c554aef65cdb431b059753a9..5ba4271c08a4244ba5f359e9c46e01eff6c6d112 100644
--- a/aleksis/core/util/predicates.py
+++ b/aleksis/core/util/predicates.py
@@ -154,3 +154,9 @@ def contains_site_preference_value(section: str, pref: str, value: str):
 def has_activated_2fa(user: User) -> bool:
     """Check if the user has activated two-factor authentication."""
     return user_has_device(user)
+
+
+@predicate
+def is_assigned_to_current_person(user: User, obj: Model) -> bool:
+    """Check if the object is assigned to the current person."""
+    return getattr(obj, "person", None) == user.person
diff --git a/aleksis/core/views.py b/aleksis/core/views.py
index 086f5d290f416e1e093f0d7363bdaed347f8824e..e8356a1a6a9f5542e4aaf40aab9a8d42adcc8e6b 100644
--- a/aleksis/core/views.py
+++ b/aleksis/core/views.py
@@ -135,6 +135,7 @@ from .util.core_helpers import (
     get_site_preferences,
     has_person,
     objectgetter_optional,
+    queryset_rules_filter,
 )
 from .util.forms import PreferenceLayout
 from .util.pdf import render_pdf
@@ -1583,10 +1584,10 @@ class ICalFeedView(DetailView):
 class ICalFeedListView(PermissionRequiredMixin, ListView):
     model = PersonalICalUrl
     template_name = "core/ical/ical_list.html"
-    permission_required = "core.use_ical_rule"
+    permission_required = "core.view_ical_rule"
 
     def get_queryset(self):
-        return PersonalICalUrl.objects.filter(person=self.request.user.person)
+        return self.model.objects.filter(person=self.request.user)
 
 
 class ICalFeedEditView(PermissionRequiredMixin, AdvancedEditView):
@@ -1594,29 +1595,17 @@ class ICalFeedEditView(PermissionRequiredMixin, AdvancedEditView):
     template_name = "core/ical/ical_edit.html"
     success_url = reverse_lazy("ical_feed_list")
     success_message = _("ICal feed updated successfully")
-    permission_required = "core.use_ical_rule"
+    permission_required = "core.edit_ical_rule"
 
     fields = ["name", "ical_feed"]
 
-    def get_queryset(self):
-        return PersonalICalUrl.objects.filter(person=self.request.user.person)
-
-    def form_valid(self, form):
-        obj = form.save(commit=False)
-        obj.person = self.request.user.person
-        obj.save()
-        return super().form_valid(form)
-
 
 class ICalFeedDeleteView(PermissionRequiredMixin, AdvancedDeleteView):
     model = PersonalICalUrl
     template_name = "core/pages/delete.html"
     success_url = reverse_lazy("ical_feed_list")
     success_message = _("ICal feed deleted successfully")
-    permission_required = "core.use_ical_rule"
-
-    def get_queryset(self):
-        return PersonalICalUrl.objects.filter(person=self.request.user.person)
+    permission_required = "core.delete_ical_rule"
 
 
 class ICalFeedCreateView(PermissionRequiredMixin, AdvancedCreateView):
@@ -1624,7 +1613,7 @@ class ICalFeedCreateView(PermissionRequiredMixin, AdvancedCreateView):
     template_name = "core/ical/ical_create.html"
     success_url = reverse_lazy("ical_feed_list")
     success_message = _("ICal feed created successfully")
-    permission_required = "core.use_ical_rule"
+    permission_required = "core.create_ical_rule"
 
     fields = ["name", "ical_feed"]