diff --git a/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java b/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java
index f558766a8b2d11b71a7ec862acaaeeb3363f6fbe..4d3576ff715f528263eba3220b8dbe282d743723 100644
--- a/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java
+++ b/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java
@@ -376,6 +376,26 @@ public class GitlabService {
         return groupApi.getSubGroups(groupId);
     }
 
+    public boolean isMember(long gitProjectId, at.ac.uibk.gitsearch.domain.User user) throws GitLabApiException {
+        try {
+            org.gitlab4j.api.models.User gitUser = this.gitLabRepository.getAdminGitLabApi().getUserApi().getUserByEmail(user.getEmail());
+
+            if (gitUser == null) {
+                return false;
+            }
+
+            return gitLabRepository.getAdminGitLabApi().getProjectApi().getMember(gitProjectId, gitUser.getId(), true) != null;
+        } catch (final GitLabApiException e) {
+            if (e.getHttpStatus() == 404) {
+                return false;
+            }
+            throw e;
+        } catch (IllegalArgumentException e) {
+            // email of user invalid
+            return false;
+        }
+    }
+
     /**
      * Utility function used to set the default JGIT credentials to use the provided
      * OAuth2 token
diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/ExerciseResource.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/ExerciseResource.java
index 82e7681a343ca1175aa38dd1c97e6ecc3c657ef2..70301c240cd99e1973bad7946db2e8952eeb375e 100644
--- a/src/main/java/at/ac/uibk/gitsearch/web/rest/ExerciseResource.java
+++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/ExerciseResource.java
@@ -1,5 +1,6 @@
 package at.ac.uibk.gitsearch.web.rest;
 
+import at.ac.uibk.gitsearch.domain.User;
 import at.ac.uibk.gitsearch.es.model.ArtemisExerciseInfo;
 import at.ac.uibk.gitsearch.security.SecurityUtils;
 import at.ac.uibk.gitsearch.service.ArtemisImportError;
@@ -8,6 +9,7 @@ import at.ac.uibk.gitsearch.service.GitlabService;
 import at.ac.uibk.gitsearch.service.SearchService;
 import at.ac.uibk.gitsearch.service.SearchService.ExtractionDepth;
 import at.ac.uibk.gitsearch.service.StatisticsService;
+import at.ac.uibk.gitsearch.service.UserService;
 import at.ac.uibk.gitsearch.service.dto.StatisticsDTO;
 import at.ac.uibk.gitsearch.web.rest.utils.RestUtils;
 import at.ac.uibk.gitsearch.web.util.HeaderUtil;
@@ -82,6 +84,10 @@ public class ExerciseResource {
     @SuppressWarnings({ "PMD.ImmutableField", "PMD.AvoidDuplicateLiterals" })
     private SearchService searchService;
 
+    @Autowired
+    @SuppressWarnings({ "PMD.ImmutableField", "PMD.AvoidDuplicateLiterals" })
+    private UserService userService;
+
     @Autowired
     @SuppressWarnings({ "PMD.ImmutableField", "PMD.AvoidDuplicateLiterals" })
     private StatisticsService statisticsService;
@@ -348,6 +354,35 @@ public class ExerciseResource {
         return ResponseEntity.ok(new URL(baseUrl + "/import/" + exerciseImportService.getTokenFromUrl(exerciseUrl)).toURI());
     }
 
+    @GetMapping("/exercises/{id}/source-authorization")
+    @SuppressWarnings("PMD.AvoidCatchingGenericException")
+    public ResponseEntity<?> getMembers(@PathVariable("id") String exerciseId) {
+        try {
+            if (SecurityUtils.getCurrentUserLogin().isEmpty()) {
+                ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+            }
+
+            final Optional<SearchResultDTO> result = searchService.findExerciseById(ExerciseId.fromString(exerciseId));
+            if (result.isEmpty()) {
+                return ResponseEntity.notFound().build();
+            }
+
+            Optional<User> user = this.userService.getUserByLogin(SecurityUtils.getCurrentUserLogin().get());
+            if (user.isEmpty()) {
+                return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+            }
+
+            int gitProjectId = result.get().getProject().getProject_id();
+
+            return this.gitLabService.isMember(gitProjectId, user.get())
+                ? ResponseEntity.ok().build()
+                : ResponseEntity.status(HttpStatus.FORBIDDEN).build();
+        } catch (final Exception e) {
+            log.error("Error while getting /source-authorization for exercise {}", exerciseId, e);
+            return ResponseEntity.internalServerError().build();
+        }
+    }
+
     /**
      * GET /exercise/imported-exercise-info/{exerciseToken} : Used to retrieve the exercise's metadata
      * of a given exerciseToken
diff --git a/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts
index a3b0f3c5279bff8f86da7b3054c51445a5692f85..17e88998a0066c3d0d306cce3fef6dff4b04bda9 100644
--- a/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts
+++ b/src/main/webapp/app/exercise/exercise-details/exercise-details.component.ts
@@ -74,12 +74,7 @@ export class ExerciseHeaderComponent {
 })
 export class ExerciseBodyComponent implements OnInit, OnDestroy {
   @Output() exerciseChangedEvent = new EventEmitter<SearchResultDTO>();
-  @Input() get referencedExercise(): SearchResultDTO | undefined {
-    return this.exercise;
-  }
-  set referencedExercise(exercise: SearchResultDTO | undefined) {
-    this.exercise = exercise as ExtendedSearchResultDTO;
-  }
+  gitlabIsAccessible = false;
   exercise: ExtendedSearchResultDTO | undefined;
 
   bookmarked = false;
@@ -92,6 +87,18 @@ export class ExerciseBodyComponent implements OnInit, OnDestroy {
   likeSubscription?: Subscription;
   authenticated = false;
 
+  @Input() get referencedExercise(): SearchResultDTO | undefined {
+    return this.exercise;
+  }
+
+  set referencedExercise(exercise: SearchResultDTO | undefined) {
+    this.gitlabIsAccessible = false;
+    this.exercise = exercise as ExtendedSearchResultDTO;
+    if (exercise) {
+      this.updateGitIsAccessibleForUser();
+    }
+  }
+
   treeIcon = faFolder;
   oerLink?: string;
   oerExerciseMatch = /([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})$/;
@@ -128,6 +135,26 @@ export class ExerciseBodyComponent implements OnInit, OnDestroy {
     );
   }
 
+  updateGitIsAccessibleForUser(): void {
+    const projectIsPrivate: boolean = this.exercise?.project.visibilty == 'private';
+    const exceptIsEmpty: boolean =
+      !this.exercise?.metadata.publicVisibility?.except || this.exercise?.metadata.publicVisibility?.except.length == 0;
+
+    if (!projectIsPrivate && exceptIsEmpty) {
+      this.gitlabIsAccessible = true;
+      return;
+    }
+
+    if (!this.isAuthenticated()) {
+      this.gitlabIsAccessible = false;
+      return;
+    }
+
+    this.exerciseService
+      .hasUserAccessToGitlabRepo(this.exercise!.exerciseId)
+      .subscribe((accessible: boolean) => (this.gitlabIsAccessible = accessible));
+  }
+
   public startAction(action: PluginActionInfo, exercise: SearchResultDTO): void {
     const basketInfo: ShoppingBasketInfo = {
       plugin: action.plugin,
diff --git a/src/main/webapp/app/exercise/exercise-details/exerciseComponents/exercise-body.component.html b/src/main/webapp/app/exercise/exercise-details/exerciseComponents/exercise-body.component.html
index 749278916080b3bf124327c09de78f7d73f36a72..f1582e368d580259728b9a489738511862394c1c 100644
--- a/src/main/webapp/app/exercise/exercise-details/exerciseComponents/exercise-body.component.html
+++ b/src/main/webapp/app/exercise/exercise-details/exerciseComponents/exercise-body.component.html
@@ -168,6 +168,7 @@
         jhiTranslate="exercise.details.allExercises"
       ></button>
       <button
+        [disabled]="!this.gitlabIsAccessible"
         type="button"
         class="btn btn-outline-secondary"
         style="display: block"
diff --git a/src/main/webapp/app/exercise/service/exercise.service.ts b/src/main/webapp/app/exercise/service/exercise.service.ts
index d27dc0e58e58d3e4d77564df2223ffa628d0aa6a..2d4a0a6349c43b3f128ce84c17d93b92d3d76db0 100644
--- a/src/main/webapp/app/exercise/service/exercise.service.ts
+++ b/src/main/webapp/app/exercise/service/exercise.service.ts
@@ -1,4 +1,4 @@
-import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
+import { HttpClient, HttpHeaders, HttpParams, HttpResponse } from '@angular/common/http';
 import { Injectable } from '@angular/core';
 import { ApplicationConfigService } from 'app/core/config/application-config.service';
 import { LikesService } from 'app/entities/likes/likes.service';
@@ -6,7 +6,8 @@ import { StatisticsService } from 'app/entities/statistics/statistics.service';
 import { SearchService } from 'app/search/service/search-service';
 import { ArtemisExerciseInfo } from 'app/shared/model/artemis-exercise-info.model';
 import { ExtendedSearchResultDTO, SearchResultDTO } from 'app/shared/model/search/search-result-dto.model';
-import { Observable } from 'rxjs';
+import { Observable, of } from 'rxjs';
+import { catchError, map } from 'rxjs/operators';
 
 @Injectable({ providedIn: 'root' })
 export class ExerciseService {
@@ -33,6 +34,20 @@ export class ExerciseService {
     });
   }
 
+  public hasUserAccessToGitlabRepo(exerciseId: string): Observable<boolean> {
+    const endpoint: string = this.applicationConfigService.getEndpointFor(
+      SERVER_API_URL + 'api/exercises/' + encodeURIforExerciseId(exerciseId) + '/source-authorization'
+    );
+    return this.http.get<any>(endpoint, { observe: 'response' }).pipe(
+      map((response: HttpResponse<any>) => {
+        return response.status === 200;
+      }),
+      catchError(() => {
+        return of(false);
+      })
+    );
+  }
+
   public loadExercise(exerciseId: string): Observable<SearchResultDTO> {
     return this.http.get<SearchResultDTO>(this.exerciseUrl + encodeURIforExerciseId(exerciseId));
   }
@@ -65,18 +80,6 @@ export class ExerciseService {
         },
         error: () => console.warn('Could not load if user has liked or not'),
       });
-
-      /*
-      * not relevant: is now reloaded directly from index
-      this.searchService.getStatisticsForExercise(exercise.exerciseId).subscribe({
-        next: (data: Statistics) => {
-          result.views = data.views!;
-          result.downloads = data.downloads!;
-          result.badgeRewarded = data.badgeRewarded!;
-        },
-        error: () => console.warn('Could not load exercise statistics'),
-      });
-      */
     }
     return result;
   }
diff --git a/src/main/webapp/app/shared/model/search/project-dto.model.ts b/src/main/webapp/app/shared/model/search/project-dto.model.ts
index 4b62548d9f9e7a96d4bb7ff3d21de711ba1f4aa9..713eb524ee56d690e2b20224608bb36244e5844f 100644
--- a/src/main/webapp/app/shared/model/search/project-dto.model.ts
+++ b/src/main/webapp/app/shared/model/search/project-dto.model.ts
@@ -5,5 +5,6 @@ export interface ProjectDTO {
   main_group: string;
   sub_group: string;
   url: string;
+  visibilty: string;
   last_activity_at: Date;
 }
diff --git a/src/main/webapp/app/shared/model/search/public-visibility-dto.model.ts b/src/main/webapp/app/shared/model/search/public-visibility-dto.model.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6ece5e0585ec545423b93205c8cea099d3197411
--- /dev/null
+++ b/src/main/webapp/app/shared/model/search/public-visibility-dto.model.ts
@@ -0,0 +1,3 @@
+export interface PublicVisibilityDTOModel {
+  except: string[];
+}
diff --git a/src/main/webapp/app/shared/model/search/user-provided-metadata-dto.model.ts b/src/main/webapp/app/shared/model/search/user-provided-metadata-dto.model.ts
index bab099bfcb851cb33efe4338dcb667a4eb0cdae4..8d649b636c1ac6ff0298c2678951ce3797b72c74 100644
--- a/src/main/webapp/app/shared/model/search/user-provided-metadata-dto.model.ts
+++ b/src/main/webapp/app/shared/model/search/user-provided-metadata-dto.model.ts
@@ -1,5 +1,6 @@
 import { Person } from '../person.model';
 import { IInteractivityType } from '../exercise.model';
+import { PublicVisibilityDTOModel } from './public-visibility-dto.model';
 
 export interface UserProvidedMetadataDTO {
   assesses: string[];
@@ -33,4 +34,5 @@ export interface UserProvidedMetadataDTO {
   title: string;
   typicalAgeRange: string;
   version: string;
+  publicVisibility: PublicVisibilityDTOModel;
 }