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

Skip to content
Snippets Groups Projects
Commit 2386c8ad 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 75928a89
No related merge requests found
Showing
with 151 additions and 21 deletions
......@@ -4,6 +4,7 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch jest explicitly",
"program": "${workspaceFolder}/node_modules/jest/bin/jest.js",
......
package at.ac.uibk.gitsearch.edu_sharing;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
public class EduSharingApiConfiguration {
@Bean
public RestTemplate restTemplate() {
return new RestTemplateBuilder().build();
}
}
package at.ac.uibk.gitsearch.edu_sharing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class EduSharingConnector {
@Autowired
private RestTemplate restTemplate;
public void sendToApi() {}
}
......@@ -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
......
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,14 @@ public class ExerciseResource {
@SuppressWarnings({ "PMD.ImmutableField", "PMD.AvoidDuplicateLiterals" })
private SearchService searchService;
@Autowired
@SuppressWarnings({ "PMD.ImmutableField", "PMD.AvoidDuplicateLiterals" })
private GitlabService gitlabService;
@Autowired
@SuppressWarnings({ "PMD.ImmutableField", "PMD.AvoidDuplicateLiterals" })
private UserService userService;
@Autowired
@SuppressWarnings({ "PMD.ImmutableField", "PMD.AvoidDuplicateLiterals" })
private StatisticsService statisticsService;
......@@ -348,6 +358,40 @@ public class ExerciseResource {
return ResponseEntity.ok(new URL(baseUrl + "/import/" + exerciseImportService.getTokenFromUrl(exerciseUrl)).toURI());
}
@GetMapping("/exercises/**")
public ResponseEntity test() {
System.out.println("hello");
return ResponseEntity.status(HttpStatus.OK).build();
}
@GetMapping("/exercises/{id}/source-authorization")
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
......
......@@ -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,17 @@ 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 +134,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,
......
......@@ -168,6 +168,7 @@
jhiTranslate="exercise.details.allExercises"
></button>
<button
[disabled]="!this.gitlabIsAccessible"
type="button"
class="btn btn-outline-secondary"
style="display: block"
......
......@@ -19,7 +19,7 @@ export class MarkDownViewerComponent implements OnInit {
}
set exercise(exercise: SearchResultDTO | undefined) {
this.myExercise = exercise;
this.loadVisible();
//this.loadVisible();
}
/*
......
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;
}
......
......@@ -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