diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index ebbc04a158ae63f7ece500b7ac515d3f6b224c56..5af04cec332f7f1f15ed0f87097927f82a157201 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -22,6 +22,7 @@ Added
 * Global calendar system
 * Calendar for birthdays of persons
 * Holiday model to track information about holidays.
+* Frontend for managing holidays.
 * [Dev] Components for implementing standard CRUD operations in new frontend.
 * [Dev] Options for filtering and sorting of GraphQL queries at the server.
 * [Dev] Managed models for instances handled by other apps.
@@ -32,6 +33,7 @@ Changed
 ~~~~~~~
 
 * Management of school terms was migrated to new frontend.
+* [Dev] Child groups are exposed in the GraphQL type for groups.
 
 Fixed
 ~~~~~
@@ -49,8 +51,6 @@ Changed
 ~~~~~~~
 
 * uWSGI is now installed together with AlekSIS-Core per default.
-* Introduce Holiday model to track information about holidays.
-* Created field for child groups in the GraphQL type for groups.
 
 Fixed
 ~~~~~
@@ -63,8 +63,8 @@ Fixed
 * [Docker] Clear obsolete bundle parts when adding apps using ONBUILD
 * Extensible forms that used a subset of fields did not render properly
 
-`3.1.1` - 2023-07-01
---------------------
+`3.1.1`_ - 2023-07-01
+---------------------
 
 Fixed
 ~~~~~
diff --git a/aleksis/core/frontend/components/holiday/HolidayInlineList.vue b/aleksis/core/frontend/components/holiday/HolidayInlineList.vue
index a7330f94db9b752c0fe80d5bb4fd6b1652a87b6e..3f4142fb35131b7e4304c645cb498b1bcfd93393 100644
--- a/aleksis/core/frontend/components/holiday/HolidayInlineList.vue
+++ b/aleksis/core/frontend/components/holiday/HolidayInlineList.vue
@@ -14,8 +14,10 @@ import DateField from "../generic/forms/DateField.vue";
     :gql-delete-mutation="gqlDeleteMutation"
     :gql-delete-multiple-mutation="gqlDeleteMultipleMutation"
     :default-item="defaultItem"
+    item-title-attribute="holidayName"
     ref="crudList"
   >
+    <!-- eslint-disable-next-line vue/valid-v-slot -->
     <template #holidayName.field="{ attrs, on, isCreate }">
       <div aria-required="true">
         <v-text-field
@@ -30,6 +32,7 @@ import DateField from "../generic/forms/DateField.vue";
     <template #dateStart="{ item }">
       {{ $d(new Date(item.dateStart), "short") }}
     </template>
+    <!-- eslint-disable-next-line vue/valid-v-slot -->
     <template #dateStart.field="{ attrs, on, item, isCreate }">
       <div aria-required="true">
         <date-field
@@ -45,6 +48,7 @@ import DateField from "../generic/forms/DateField.vue";
     <template #dateEnd="{ item }">
       {{ $d(new Date(item.dateEnd), "short") }}
     </template>
+    <!-- eslint-disable-next-line vue/valid-v-slot -->
     <template #dateEnd.field="{ attrs, on, item }">
       <div aria-required="true">
         <date-field
@@ -102,21 +106,15 @@ export default {
   },
   methods: {
     updateEndDate(newStartDate, item, isCreate) {
-      console.log("method called", item);
       let start = new Date(newStartDate);
-      console.log(start);
       if (!item.endDate) {
         if (isCreate) {
           this.$refs.crudList.createModel.dateEnd = newStartDate;
-          console.log("Changed of createmodel");
         } else {
           this.$refs.crudList.editableItems.find(
             (holiday) => holiday.id === item.id
           )[0].dateEnd = newStartDate;
-          console.log("Changed of editableitems");
         }
-      } else {
-        console.log(item, newStartDate);
       }
     },
   },
diff --git a/aleksis/core/frontend/messages/en.json b/aleksis/core/frontend/messages/en.json
index a477736639405a1005f8799ce09b6c88f45a95de..0f23b7a24df67f7b5cafd999628b207dcb87f3de 100644
--- a/aleksis/core/frontend/messages/en.json
+++ b/aleksis/core/frontend/messages/en.json
@@ -317,5 +317,14 @@
   },
   "selection": {
     "num_items_selected": "No items selected | 1 item selected | {n} items selected"
+  },
+  "holidays": {
+    "menu_title": "Holidays",
+    "title": "Holiday",
+    "title_plural": "Holidays",
+    "create_holiday": "Create Holiday",
+    "date_start": "Start Date",
+    "date_end": "End Date",
+    "holiday_name": "Name"
   }
 }
diff --git a/aleksis/core/frontend/routes.js b/aleksis/core/frontend/routes.js
index e8bc2e6a6232172e27c68daeb32d2410b8e0a04f..cffb6955b7c607d6d49fc65642a3a52dd30b5a61 100644
--- a/aleksis/core/frontend/routes.js
+++ b/aleksis/core/frontend/routes.js
@@ -371,7 +371,7 @@ const routes = [
           inMenu: true,
           titleKey: "holidays.menu_title",
           icon: "$holidays",
-          permission: "core.view_holiday_rule",
+          permission: "core.view_holidays_rule",
         },
       },
       {
diff --git a/aleksis/core/rules.py b/aleksis/core/rules.py
index ae2f12129cd8193cb0f5fba9887a6a45c2c92785..2251a504434052f5faaf94c91cd0317cd68ee21c 100644
--- a/aleksis/core/rules.py
+++ b/aleksis/core/rules.py
@@ -1,7 +1,7 @@
 import rules
 from rules import is_superuser
 
-from .models import AdditionalField, Announcement, Group, GroupType, Person
+from .models import AdditionalField, Announcement, Group, GroupType, Holiday, Person
 from .util.predicates import (
     has_any_object,
     has_global_perm,
@@ -412,3 +412,28 @@ rules.add_perm("core.view_progress_rule", view_progress_predicate)
 
 view_calendar_feed_predicate = has_person
 rules.add_perm("core.view_calendar_feed_rule", view_calendar_feed_predicate)
+
+# Holidays
+
+view_holiday_predicate = has_person & (
+    has_global_perm("core.view_holiday") | has_object_perm("core.view_holiday")
+)
+rules.add_perm("core.view_holiday_rule", view_holiday_predicate)
+
+view_holidays_predicate = has_person & (
+    has_global_perm("core.view_holiday") | has_any_object("core.view_holiday", Holiday)
+)
+rules.add_perm("core.view_holidays_rule", view_holidays_predicate)
+
+edit_holiday_predicate = has_person & (
+    has_global_perm("core.change_holiday") | has_object_perm("core.change_holiday")
+)
+rules.add_perm("core.edit_holiday_rule", edit_holiday_predicate)
+
+create_holiday_predicate = has_person & (has_global_perm("core.add_holiday"))
+rules.add_perm("core.create_holiday_rule", create_holiday_predicate)
+
+delete_holiday_predicate = has_person & (
+    has_global_perm("core.delete_holiday") | has_object_perm("core.delete_holiday")
+)
+rules.add_perm("core.delete_holiday_rule", delete_holiday_predicate)
diff --git a/aleksis/core/schema/__init__.py b/aleksis/core/schema/__init__.py
index a1c2896ef44220326d851e81ba53037db3a70588..9db010c02eaa5d632ea6e1f443f9b7a0582b25e5 100644
--- a/aleksis/core/schema/__init__.py
+++ b/aleksis/core/schema/__init__.py
@@ -145,14 +145,12 @@ class Query(graphene.ObjectType):
     def resolve_group_by_id(root, info, id):  # noqa
         group = Group.objects.filter(id=id)
 
-        if len(group) != 1:
-            return None
-
-        group = group.first()
+        if group.exists():
+            group = group.first()
 
-        if not info.context.user.has_perm("core.view_group", group):
-            raise PermissionDenied()
-        return group
+            if not info.context.user.has_perm("core.view_group_rule", group):
+                raise PermissionDenied()
+            return group
 
     def resolve_who_am_i(root, info, **kwargs):
         return info.context.user
diff --git a/aleksis/core/schema/holiday.py b/aleksis/core/schema/holiday.py
index b742b373e8f368ae2f6760f1361340b36a07fa7a..4ac8e82399b0227f6908e783ef7e9e27bb4a83cd 100644
--- a/aleksis/core/schema/holiday.py
+++ b/aleksis/core/schema/holiday.py
@@ -4,6 +4,7 @@ from graphene_django_cud.mutations import (
     DjangoBatchPatchMutation,
     DjangoCreateMutation,
 )
+from guardian.shortcuts import get_objects_for_user
 
 from ..models import Holiday
 from .base import (
@@ -28,29 +29,29 @@ class HolidayType(PermissionsTypeMixin, DjangoFilterMixin, DjangoObjectType):
 
     @classmethod
     def get_queryset(cls, queryset, info):
-        return queryset  # FIXME filter this queryset based on permissions
+        return get_objects_for_user(info.context.user, "core.view_holiday", queryset)
 
 
 class HolidayCreateMutation(DjangoCreateMutation):
     class Meta:
         model = Holiday
-        permissions = ("core.create_holiday",)
+        permissions = ("core.create_holiday_rule",)
         only_fields = ("holiday_name", "date_start", "date_end")
 
 
 class HolidayDeleteMutation(DeleteMutation):
     klass = Holiday
-    permission_required = "core.delete_holiday"
+    permission_required = "core.delete_holiday_rule"
 
 
 class HolidayBatchDeleteMutation(PermissionBatchDeleteMixin, DjangoBatchDeleteMutation):
     class Meta:
         model = Holiday
-        permissions = ("core.delete_holiday",)
+        permissions = ("core.delete_holiday_rule",)
 
 
 class HolidayBatchPatchMutation(PermissionBatchPatchMixin, DjangoBatchPatchMutation):
     class Meta:
         model = Holiday
-        permissions = ("core.change_holiday",)
-        only_fields = ("holiday_name", "date_start", "date_end")
+        permissions = ("core.edit_holiday_rule",)
+        only_fields = ("id", "holiday_name", "date_start", "date_end")