diff --git a/resources/assets/stylesheets/less/pagination.less b/resources/assets/stylesheets/less/pagination.less
index c6b3498b868bd966fa883420f105107b360b5de8..6e1bf9fff3d87f3a1c1649d792563c37dd827630 100644
--- a/resources/assets/stylesheets/less/pagination.less
+++ b/resources/assets/stylesheets/less/pagination.less
@@ -12,7 +12,9 @@
 .pagination {
     li {
         display: inline-block;
+    }
 
+    li:not(.no-divider) {
         &:not(:first-of-type) {
             &::before {
                 content: ' | ';
@@ -57,3 +59,13 @@
         .background-icon('arr_1right');
     }
 }
+
+.pagination-wrapper-flex {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    .pagination {
+        margin-left: auto;
+    }
+}
+
diff --git a/resources/vue/components/StudipPagination.vue b/resources/vue/components/StudipPagination.vue
new file mode 100644
index 0000000000000000000000000000000000000000..8b82f989460b82149c1c4a20ba12925a607421ba
--- /dev/null
+++ b/resources/vue/components/StudipPagination.vue
@@ -0,0 +1,101 @@
+<template>
+    <div class="pagination-wrapper-flex">
+        <p :id="pagination_id" class="audible">
+            {{ $gettext('Blättern') }}
+        </p>
+        <ul class="pagination" role="navigation" :aria-labelledby="pagination_id">
+            <li class="prev" v-if="currentOffset > 0">
+                <button class="pagination--link" @click.prevent="goBack" rel="prev" :title="$gettext('Zurück')">
+                    <span class="audible">{{ $gettext('Eine Seite') }}</span>
+                    {{ $gettext('zurück') }}
+                </button>
+            </li>
+            <template v-for="offset of offsets">
+                <li :key="'end-dots-' + offset" class="divider"
+                    v-if="offset === (total_offsets - 1) && currentOffset < (total_offsets - 1) - (range + 1)">
+                    &hellip;
+                </li>
+                <li :key="'offset-' + offset" :class="{'current': offset === currentOffset, 'no-divider': offset === 0}">
+                    <button class="pagination--link" @click.prevent="goTo(offset)">
+                        <span class="audible">{{ $gettext('Seite') }}</span>
+                        {{ offset + 1 }}
+                    </button>
+                </li>
+                <li :key="'start-dots' + offset" class="divider"
+                    v-if="offset === 0 && currentOffset > range + 1">
+                    &hellip;
+                </li>
+            </template>
+            <li class="next no-divider" v-if="currentOffset < total_offsets - 1">
+                <button class="pagination--link" @click.prevent="goNext" rel="next" :title="$gettext('Weiter')">
+                    <span class="audible">{{ $gettext('Eine Seite') }}</span>
+                    {{ $gettext('weiter') }}
+                </button>
+            </li>
+        </ul>
+    </div>
+</template>
+
+<script>
+export default {
+    name: 'studip-pagination',
+    props: {
+        currentOffset: {
+            type: Number,
+            required: true
+        },
+        totalItems: {
+            type: Number,
+            required: true
+        },
+        itemsPerPage: {
+            type: Number,
+            required: true
+        },
+        range: {
+            type: Number,
+            default: 2,
+            min: 1
+        }
+    },
+    computed: {
+        pagination_id() {
+            return 'pagination-label-' + this._uid;
+        },
+        total_offsets() {
+            let total = Math.ceil(this.totalItems / this.itemsPerPage);
+            return total;
+        },
+        offsets() {
+            let offsets = [0, this.currentOffset, (this.total_offsets - 1)];
+            for (let i = 1; i <= this.range; i++) {
+                offsets.push(this.currentOffset - i);
+                offsets.push(this.currentOffset + i);
+            }
+            offsets = offsets.map(item => parseInt(item, 10));
+            offsets = [...new Set(offsets)];
+            offsets = offsets.filter(item => item >= 0 && item < this.total_offsets);
+            offsets.sort((a, b) => a - b);
+            return offsets;
+        },
+
+    },
+    methods: {
+        goBack() {
+            this.updateOffset(this.currentOffset - 1);
+        },
+        goNext() {
+            this.updateOffset(this.currentOffset + 1);
+        },
+        goTo(selected) {
+            if (selected === this.currentOffset) {
+                return;
+            }
+            this.updateOffset(selected);
+        },
+        updateOffset(offset) {
+            this.$emit('updateOffset', parseInt(offset, 10));
+        }
+    }
+}
+</script>
diff --git a/resources/vue/components/courseware/CoursewareStructuralElementPermissions.vue b/resources/vue/components/courseware/CoursewareStructuralElementPermissions.vue
index c061ba3763c7fa85ba3c8edcbcbfc9af1d3d0a1b..473b82f530ceb5a338602edc69a167899c7aaeb3 100644
--- a/resources/vue/components/courseware/CoursewareStructuralElementPermissions.vue
+++ b/resources/vue/components/courseware/CoursewareStructuralElementPermissions.vue
@@ -14,20 +14,24 @@
                 <translate>Studierende</translate>
             </caption>
             <colgroup>
-                <col style="width:20%" />
-                <col style="width:35%" />
-                <col style="width:45%" />
+                <col style="width:1%" />
+                <col style="width:19%" />
+                <col style="width:1%" />
+                <col style="width:29%" />
+                <col style="width:50%" />
             </colgroup>
             <thead>
                 <tr>
+                    <th><input type="checkbox" v-model="bulkSelectAutorRead" @click="handleBulkSelectRead($event, 'autor')"/></th>
                     <th><translate>Lesen</translate></th>
+                    <th><input type="checkbox" v-model="bulkSelectAutorWrite" @click="handleBulkSelectWrite($event)"/></th>
                     <th><translate>Lesen und Schreiben</translate></th>
                     <th><translate>Name</translate></th>
                 </tr>
             </thead>
             <tbody>
-                <tr v-for="user in autor_members" :key="user.user_id">
-                    <td class="perm">
+                <tr v-for="user in autor_members_filtered" :key="user.user_id">
+                    <td class="perm" colspan="2">
                         <input
                             type="checkbox"
                             :id="user.user_id + `_read`"
@@ -35,7 +39,7 @@
                             v-model="userPermsReadUsers"
                         />
                     </td>
-                    <td class="perm">
+                    <td class="perm" colspan="2">
                         <input
                             type="checkbox"
                             :id="user.user_id + `_write`"
@@ -52,6 +56,17 @@
                     </td>
                 </tr>
             </tbody>
+            <tfoot v-if="can_paginate && autor_members.length > entries_per_page">
+                <tr>
+                    <td colspan="5">
+                        <studip-pagination
+                            :currentOffset="autorOffset"
+                            :totalItems="autor_members.length"
+                            :itemsPerPage="entries_per_page"
+                            @updateOffset="updateAutorOffset" />
+                    </td>
+                </tr>
+            </tfoot>
         </table>
 
         <table class="default" v-if="user_members.length">
@@ -59,22 +74,24 @@
                 <translate>Leser/-innen</translate>
             </caption>
             <colgroup>
-                <col style="width:55%" />
-                <col style="width:45%" />
+                <col style="width:1%" />
+                <col style="width:39%" />
+                <col style="width:50%" />
             </colgroup>
             <thead>
                 <tr>
+                    <th><input type="checkbox" v-model="bulkSelectUserRead" @click="handleBulkSelectRead($event, 'user')"/></th>
                     <th><translate>Lesen</translate></th>
                     <th><translate>Name</translate></th>
                 </tr>
             </thead>
             <tbody>
-                <tr v-for="user in user_members" :key="user.user_id">
-                    <td>
+                <tr v-for="user in user_members_filtered" :key="user.user_id">
+                    <td colspan="2">
                         <input
                             type="checkbox"
                             :id="user.user_id + `_read`"
-                            :value="user.id"
+                            :value="user.user_id"
                             v-model="userPermsReadUsers"
                         />
                     </td>
@@ -87,6 +104,17 @@
                     </td>
                 </tr>
             </tbody>
+            <tfoot v-if="can_paginate && user_members.length > entries_per_page">
+                <tr>
+                    <td colspan="3">
+                        <studip-pagination
+                            :currentOffset="userOffset"
+                            :totalItems="user_members.length"
+                            :itemsPerPage="entries_per_page"
+                            @updateOffset="updateUserOffset"/>
+                    </td>
+                </tr>
+            </tfoot>
         </table>
 
         <table class="default" v-if="groups.length">
@@ -135,6 +163,8 @@
     </div>
 </template>
 <script>
+import StudipPagination from './../StudipPagination.vue';
+
 import { mapActions, mapGetters } from 'vuex';
 
 export default {
@@ -142,6 +172,9 @@ export default {
     props: {
         element: Object,
     },
+    components: {
+        StudipPagination
+    },
     data() {
         return {
             user_perms: {},
@@ -151,6 +184,11 @@ export default {
             userPermsWriteUsers: [],
             userPermsWriteGroups: [],
             userPermsWriteAll: Boolean,
+            bulkSelectAutorRead: false,
+            bulkSelectUserRead: false,
+            bulkSelectAutorWrite: false,
+            userOffset: 0,
+            autorOffset: 0,
         };
     },
 
@@ -181,11 +219,21 @@ export default {
         // load memberships for coursewares in a course context
         if (this.context.type === 'courses') {
             const parent = { type: 'courses', id: this.context.id };
-            this.loadCourseMemberships({ parent, relationship: 'memberships', options: { include: 'user' } });
+            let options = {
+                include: 'user',
+                'page[limit]': 10000,
+            }
+            this.loadCourseMemberships({ parent, relationship: 'memberships', options: options });
             this.loadCourseStatusGroups({ parent, relationship: 'status-groups' });
         }
     },
 
+    updated () {
+        this.handleBulkSelectReadPassive('autor');
+        this.handleBulkSelectReadPassive('user');
+        this.handleBulkSelectWritePassive();
+    },
+
     computed: {
         ...mapGetters({
             context: 'context',
@@ -240,6 +288,15 @@ export default {
             return members;
         },
 
+        autor_members_filtered() {
+            if (this.autor_members.length === 0) {
+                return [];
+            }
+            let start = this.autorOffset * this.entries_per_page;
+            let end = ((this.autorOffset + 1) * this.entries_per_page);
+            return this.autor_members.slice(start, end);
+        },
+
         user_members() {
             if (Object.keys(this.users).length === 0 && this.users.constructor === Object) {
                 return [];
@@ -252,6 +309,23 @@ export default {
             return members;
         },
 
+        user_members_filtered() {
+            if (this.user_members.length === 0) {
+                return [];
+            }
+            let start = this.userOffset * this.entries_per_page;
+            let end = ((this.userOffset + 1) * this.entries_per_page);
+            return this.user_members.slice(start, end);
+        },
+
+        entries_per_page() {
+            return STUDIP?.config?.ENTRIES_PER_PAGE ?? 0;
+        },
+
+        can_paginate() {
+            return this.entries_per_page > 0;
+        },
+
         readApproval() {
             return {
                 all: this.userPermsReadAll,
@@ -274,10 +348,93 @@ export default {
             loadCourseMemberships: 'course-memberships/loadRelated',
             loadCourseStatusGroups: 'status-groups/loadRelated',
         }),
+
+        updateAutorOffset(offset) {
+            this.autorOffset = parseInt(offset);
+        },
+
+        updateUserOffset(offset) {
+            this.userOffset = parseInt(offset);
+        },
+
+        handleBulkSelectRead(event, type) {
+            let state = event.target.checked;
+            let list = type === 'autor' ? this.autor_members_filtered : this.user_members_filtered;
+            if (list.length == 0) {
+                return;
+            }
+            if (type === 'autor') {
+                this.bulkSelectAutorRead = state;
+            } else {
+                this.bulkSelectUserRead = state;
+            }
+            for (let user of list) {
+                if (state) { // Add
+                    if (this.userPermsReadUsers.includes(user.user_id) === false) {
+                        this.userPermsReadUsers.push(user.user_id);
+                    }
+                } else { // Remove
+                    if (this.userPermsReadUsers.includes(user.user_id) === true) {
+                        let index = this.userPermsReadUsers.findIndex((perm) => perm == user.user_id);
+                        this.userPermsReadUsers.splice(index, 1);
+                    }
+                }
+            }
+        },
+
+        handleBulkSelectWrite(event) {
+            let state = event.target.checked;
+            let list = this.autor_members_filtered;
+            if (list.length == 0) {
+                return;
+            }
+            this.bulkSelectAutorWrite = state;
+            for (let user of list) {
+                if (state) { // Add
+                    if (this.userPermsWriteUsers.includes(user.user_id) === false) {
+                        this.userPermsWriteUsers.push(user.user_id);
+                    }
+                } else { // Remove
+                    if (this.userPermsWriteUsers.includes(user.user_id) === true) {
+                        let index = this.userPermsWriteUsers.findIndex((perm) => perm == user.user_id);
+                        this.userPermsWriteUsers.splice(index, 1);
+                    }
+                }
+            }
+        },
+
+        handleBulkSelectReadPassive(type) {
+            let bulkState = false;
+            if (type === 'autor' && this.autor_members_filtered?.length > 0) {
+                let currentAutorsIds = this.autor_members_filtered.map((autor) => autor.user_id);
+                if (currentAutorsIds.every((id) => this.userPermsReadUsers.includes(id))) {
+                    bulkState = true;
+                }
+                this.bulkSelectAutorRead = bulkState;
+            }
+            if (type === 'user' && this.user_members_filtered?.length > 0) {
+                let currentUsersIds = this.user_members_filtered.map((user) => user.user_id);
+                if (currentUsersIds.every((id) => this.userPermsReadUsers.includes(id))) {
+                    bulkState = true;
+                }
+                this.bulkSelectUserRead = bulkState;
+            }
+        },
+
+        handleBulkSelectWritePassive() {
+            let bulkState = false;
+            let currentAutorsIds = this.autor_members_filtered.map((autor) => autor.user_id);
+            if (currentAutorsIds.every((id) => this.userPermsWriteUsers.includes(id))) {
+                bulkState = true;
+            }
+            this.bulkSelectAutorWrite = bulkState;
+        }
     },
 
     watch: {
         userPermsReadUsers(newVal, oldVal) {
+            this.handleBulkSelectReadPassive('autor');
+            this.handleBulkSelectReadPassive('user');
             this.$emit('updateReadApproval', this.readApproval);
         },
         userPermsReadGroups(newVal, oldVal) {
@@ -290,6 +447,7 @@ export default {
             }
         },
         userPermsWriteUsers(newVal, oldVal) {
+            this.handleBulkSelectWritePassive();
             this.$emit('updateWriteApproval', this.writeApproval);
         },
         userPermsWriteGroups(newVal, oldVal) {
@@ -301,6 +459,13 @@ export default {
                 this.userPermsReadAll = false;
             }
         },
+        autorOffset(newVal, oldVal) {
+            this.handleBulkSelectReadPassive('autor');
+            this.handleBulkSelectWritePassive();
+        },
+        userOffset(newVal, oldVal) {
+            this.handleBulkSelectReadPassive('user');
+        }
     },
 };
 </script>