This is the codeAbility Sharing Platform! Learn more about the codeAbility Sharing Platform.

Skip to content
Snippets Groups Projects
Commit 54925321 authored by Philipp Gritsch's avatar Philipp Gritsch
Browse files

closes #469

introduce check to only make button clickable if user has access to git repository
parent 5aee09e2
No related merge requests found
......@@ -375,6 +375,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
......
package at.ac.uibk.gitsearch.web.rest;
import at.ac.uibk.gitsearch.domain.ChildInfo;
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;
......@@ -9,6 +10,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;
......@@ -84,6 +86,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;
......@@ -389,6 +395,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
......
......@@ -83,14 +83,18 @@ export class ExerciseBodyComponent implements OnInit, OnDestroy, AfterViewInit {
@Input() get referencedExercise(): SearchResultDTO | undefined {
return this.exercise;
}
set referencedExercise(exercise: SearchResultDTO | undefined) {
const newExercise = exercise as ExtendedSearchResultDTO;
if (newExercise && newExercise.exerciseId !== this.exercise?.exerciseId) {
this.exercise = exercise as ExtendedSearchResultDTO;
this.updateParent(this.exercise);
this.updateGitIsAccessibleForUser();
}
}
exercise: ExtendedSearchResultDTO | undefined;
gitlabIsAccessible = false;
parent: SearchResultDTO | undefined;
......@@ -155,6 +159,26 @@ export class ExerciseBodyComponent implements OnInit, OnDestroy, AfterViewInit {
return '';
}
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,
......
......@@ -161,6 +161,7 @@
jhiTranslate="exercise.details.allExercises"
></button>
<button
[disabled]="!this.gitlabIsAccessible"
type="button"
class="btn btn-outline-secondary"
style="display: block"
......
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';
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 { catchError, map } from 'rxjs/operators';
import { ChildInfo, ExtendedSearchResultDTO, hasChildren, SearchResultDTO } from 'app/shared/model/search/search-result-dto.model';
import { BehaviorSubject, Observable } from 'rxjs';
import { BehaviorSubject, Observable, of } from 'rxjs';
@Injectable({ providedIn: 'root' })
export class ExerciseService {
......@@ -36,6 +37,20 @@ export class ExerciseService {
private exerciseCache: { [id: string]: BehaviorSubject<SearchResultDTO> } = {};
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> {
if (this.exerciseCache[exerciseId]) {
return this.exerciseCache[exerciseId];
......@@ -108,18 +123,6 @@ export class ExerciseService {
},
error: () => console.warn('Could not load if user has liked or not'),
});
/*
* the following is 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;
}
......
......@@ -5,5 +5,6 @@ export interface ProjectDTO {
main_group: string;
sub_group: string;
url: string;
visibilty: string;
last_activity_at: Date;
}
export interface PublicVisibilityDTOModel {
except: string[];
}
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;
}
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment