mirror of
https://github.com/Ombi-app/Ombi.git
synced 2025-07-06 13:11:13 -07:00
fix(permissions): 🐛 Improved the security around the role "Manage Own Requests" (#4397)
* Secure ManageOwnRequests API paths Fixes #4391 * Hide delete request option if user is not allowed * Refactor CheckOwnRequests * Fix deleteRequest test * Improve performance and clean up code * Fix manageOwnRequests check * Refactor CheckCanManageRequest
This commit is contained in:
parent
4410790bc0
commit
334a32bca4
18 changed files with 106 additions and 36 deletions
|
@ -78,6 +78,32 @@ namespace Ombi.Core.Engine
|
||||||
return _dbTv;
|
return _dbTv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected async Task<RequestEngineResult> CheckCanManageRequest(BaseRequest request) {
|
||||||
|
var errorResult = new RequestEngineResult {
|
||||||
|
Result = false,
|
||||||
|
ErrorCode = ErrorCode.NoPermissions
|
||||||
|
};
|
||||||
|
var successResult = new RequestEngineResult { Result = true };
|
||||||
|
|
||||||
|
// Admins can always manage requests
|
||||||
|
var isAdmin = await IsInRole(OmbiRoles.PowerUser) || await IsInRole(OmbiRoles.Admin);
|
||||||
|
if (isAdmin) {
|
||||||
|
return successResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Users with 'ManageOwnRequests' can only manage their own requests
|
||||||
|
var canManageOwnRequests = await IsInRole(OmbiRoles.ManageOwnRequests);
|
||||||
|
if (!canManageOwnRequests) {
|
||||||
|
return errorResult;
|
||||||
|
}
|
||||||
|
var isRequestedBySameUser = ( await GetUser() ).Id == request.RequestedUser?.Id;
|
||||||
|
if (isRequestedBySameUser) {
|
||||||
|
return successResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
return errorResult;
|
||||||
|
}
|
||||||
|
|
||||||
public RequestCountModel RequestCount()
|
public RequestCountModel RequestCount()
|
||||||
{
|
{
|
||||||
var movieQuery = MovieRepository.GetAll();
|
var movieQuery = MovieRepository.GetAll();
|
||||||
|
|
|
@ -18,7 +18,7 @@ namespace Ombi.Core.Engine
|
||||||
Task<int> GetTotal();
|
Task<int> GetTotal();
|
||||||
Task<RequestEngineResult> MarkAvailable(int modelId);
|
Task<RequestEngineResult> MarkAvailable(int modelId);
|
||||||
Task<RequestEngineResult> MarkUnavailable(int modelId);
|
Task<RequestEngineResult> MarkUnavailable(int modelId);
|
||||||
Task RemoveAlbumRequest(int requestId);
|
Task<RequestEngineResult> RemoveAlbumRequest(int requestId);
|
||||||
Task<RequestEngineResult> RequestAlbum(MusicAlbumRequestViewModel model);
|
Task<RequestEngineResult> RequestAlbum(MusicAlbumRequestViewModel model);
|
||||||
Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search);
|
Task<IEnumerable<AlbumRequest>> SearchAlbumRequest(string search);
|
||||||
Task<bool> UserHasRequest(string userId);
|
Task<bool> UserHasRequest(string userId);
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
Task<IEnumerable<MovieRequests>> SearchMovieRequest(string search);
|
Task<IEnumerable<MovieRequests>> SearchMovieRequest(string search);
|
||||||
Task<RequestEngineResult> RequestCollection(int collectionId, CancellationToken cancellationToken);
|
Task<RequestEngineResult> RequestCollection(int collectionId, CancellationToken cancellationToken);
|
||||||
|
|
||||||
Task RemoveMovieRequest(int requestId);
|
Task<RequestEngineResult> RemoveMovieRequest(int requestId);
|
||||||
Task RemoveAllMovieRequests();
|
Task RemoveAllMovieRequests();
|
||||||
Task<MovieRequests> GetRequest(int requestId);
|
Task<MovieRequests> GetRequest(int requestId);
|
||||||
Task<MovieRequests> UpdateMovieRequest(MovieRequests request);
|
Task<MovieRequests> UpdateMovieRequest(MovieRequests request);
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace Ombi.Core.Engine.Interfaces
|
||||||
Task<TvRequests> UpdateTvRequest(TvRequests request);
|
Task<TvRequests> UpdateTvRequest(TvRequests request);
|
||||||
Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId);
|
Task<IEnumerable<ChildRequests>> GetAllChldren(int tvId);
|
||||||
Task<ChildRequests> UpdateChildRequest(ChildRequests request);
|
Task<ChildRequests> UpdateChildRequest(ChildRequests request);
|
||||||
Task RemoveTvChild(int requestId);
|
Task<RequestEngineResult> RemoveTvChild(int requestId);
|
||||||
Task<RequestEngineResult> ApproveChildRequest(int id);
|
Task<RequestEngineResult> ApproveChildRequest(int id);
|
||||||
Task<IEnumerable<TvRequests>> GetRequestsLite();
|
Task<IEnumerable<TvRequests>> GetRequestsLite();
|
||||||
Task UpdateQualityProfile(int requestId, int profileId);
|
Task UpdateQualityProfile(int requestId, int profileId);
|
||||||
|
|
|
@ -654,11 +654,20 @@ namespace Ombi.Core.Engine
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="requestId">The request identifier.</param>
|
/// <param name="requestId">The request identifier.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task RemoveMovieRequest(int requestId)
|
public async Task<RequestEngineResult> RemoveMovieRequest(int requestId)
|
||||||
{
|
{
|
||||||
var request = await MovieRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId);
|
var request = await MovieRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||||
|
|
||||||
|
var result = await CheckCanManageRequest(request);
|
||||||
|
if (result.IsError)
|
||||||
|
return result;
|
||||||
|
|
||||||
await MovieRepository.Delete(request);
|
await MovieRepository.Delete(request);
|
||||||
await _mediaCacheService.Purge();
|
await _mediaCacheService.Purge();
|
||||||
|
return new RequestEngineResult
|
||||||
|
{
|
||||||
|
Result = true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveAllMovieRequests()
|
public async Task RemoveAllMovieRequests()
|
||||||
|
|
|
@ -404,10 +404,20 @@ namespace Ombi.Core.Engine
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="requestId">The request identifier.</param>
|
/// <param name="requestId">The request identifier.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task RemoveAlbumRequest(int requestId)
|
public async Task<RequestEngineResult> RemoveAlbumRequest(int requestId)
|
||||||
{
|
{
|
||||||
var request = await MusicRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId);
|
var request = await MusicRepository.GetAll().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||||
|
|
||||||
|
var result = await CheckCanManageRequest(request);
|
||||||
|
if (result.IsError)
|
||||||
|
return result;
|
||||||
|
|
||||||
await MusicRepository.Delete(request);
|
await MusicRepository.Delete(request);
|
||||||
|
|
||||||
|
return new RequestEngineResult
|
||||||
|
{
|
||||||
|
Result = true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> UserHasRequest(string userId)
|
public async Task<bool> UserHasRequest(string userId)
|
||||||
|
|
|
@ -7,7 +7,7 @@ namespace Ombi.Core.Engine
|
||||||
{
|
{
|
||||||
public bool Result { get; set; }
|
public bool Result { get; set; }
|
||||||
public string Message { get; set; }
|
public string Message { get; set; }
|
||||||
public bool IsError => !string.IsNullOrEmpty(ErrorMessage);
|
public bool IsError => ( !string.IsNullOrEmpty(ErrorMessage) || ErrorCode != null );
|
||||||
public string ErrorMessage { get; set; }
|
public string ErrorMessage { get; set; }
|
||||||
public ErrorCode? ErrorCode { get; set; }
|
public ErrorCode? ErrorCode { get; set; }
|
||||||
public int RequestId { get; set; }
|
public int RequestId { get; set; }
|
||||||
|
|
|
@ -749,10 +749,14 @@ namespace Ombi.Core.Engine
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveTvChild(int requestId)
|
public async Task<RequestEngineResult> RemoveTvChild(int requestId)
|
||||||
{
|
{
|
||||||
var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId);
|
var request = await TvRepository.GetChild().FirstOrDefaultAsync(x => x.Id == requestId);
|
||||||
|
|
||||||
|
var result = await CheckCanManageRequest(request);
|
||||||
|
if (result.IsError)
|
||||||
|
return result;
|
||||||
|
|
||||||
TvRepository.Db.ChildRequests.Remove(request);
|
TvRepository.Db.ChildRequests.Remove(request);
|
||||||
var all = TvRepository.Db.TvRequests.Include(x => x.ChildRequests);
|
var all = TvRepository.Db.TvRequests.Include(x => x.ChildRequests);
|
||||||
var parent = all.FirstOrDefault(x => x.Id == request.ParentRequestId);
|
var parent = all.FirstOrDefault(x => x.Id == request.ParentRequestId);
|
||||||
|
@ -766,6 +770,11 @@ namespace Ombi.Core.Engine
|
||||||
|
|
||||||
await TvRepository.Db.SaveChangesAsync();
|
await TvRepository.Db.SaveChangesAsync();
|
||||||
await _mediaCacheService.Purge();
|
await _mediaCacheService.Purge();
|
||||||
|
|
||||||
|
return new RequestEngineResult
|
||||||
|
{
|
||||||
|
Result = true,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RemoveTvRequest(int requestId)
|
public async Task RemoveTvRequest(int requestId)
|
||||||
|
|
|
@ -60,7 +60,7 @@
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
<button mat-raised-button color="accent" [routerLink]="'/details/artist/' + element.foreignArtistId">{{ 'Requests.Details' | translate}}</button>
|
<button mat-raised-button color="accent" [routerLink]="'/details/artist/' + element.foreignArtistId">{{ 'Requests.Details' | translate}}</button>
|
||||||
<button mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || manageOwnRequests"> {{ 'Requests.Options' | translate}}</button>
|
<button mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || ( manageOwnRequests && element.requestedUser?.userName == userName )"> {{ 'Requests.Options' | translate}}</button>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ export class AlbumsGridComponent implements OnInit, AfterViewInit {
|
||||||
public defaultOrder: string = "desc";
|
public defaultOrder: string = "desc";
|
||||||
public currentFilter: RequestFilterType = RequestFilterType.All;
|
public currentFilter: RequestFilterType = RequestFilterType.All;
|
||||||
public manageOwnRequests: boolean;
|
public manageOwnRequests: boolean;
|
||||||
|
public userName: string;
|
||||||
|
|
||||||
public RequestFilter = RequestFilterType;
|
public RequestFilter = RequestFilterType;
|
||||||
|
|
||||||
|
@ -43,6 +44,7 @@ export class AlbumsGridComponent implements OnInit, AfterViewInit {
|
||||||
constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef,
|
constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef,
|
||||||
private auth: AuthService, private storageService: StorageService) {
|
private auth: AuthService, private storageService: StorageService) {
|
||||||
|
|
||||||
|
this.userName = auth.claims().name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
|
|
|
@ -76,7 +76,7 @@
|
||||||
<th mat-header-cell *matHeaderCellDef> </th>
|
<th mat-header-cell *matHeaderCellDef> </th>
|
||||||
<td mat-cell *matCellDef="let element">
|
<td mat-cell *matCellDef="let element">
|
||||||
<button id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{ 'Requests.Details' | translate}}</button>
|
<button id="detailsButton{{element.id}}" mat-raised-button color="accent" [routerLink]="'/details/movie/' + element.theMovieDbId">{{ 'Requests.Details' | translate}}</button>
|
||||||
<button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || manageOwnRequests"> {{ 'Requests.Options' | translate}}</button>
|
<button id="optionsButton{{element.id}}" mat-raised-button color="warn" (click)="openOptions(element)" *ngIf="isAdmin || ( manageOwnRequests && element.requestedUser?.userName == userName ) "> {{ 'Requests.Options' | translate}}</button>
|
||||||
</td>
|
</td>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
|
||||||
public defaultOrder: string = "desc";
|
public defaultOrder: string = "desc";
|
||||||
public currentFilter: RequestFilterType = RequestFilterType.All;
|
public currentFilter: RequestFilterType = RequestFilterType.All;
|
||||||
public selection = new SelectionModel<IMovieRequests>(true, []);
|
public selection = new SelectionModel<IMovieRequests>(true, []);
|
||||||
|
public userName: string;
|
||||||
|
|
||||||
public RequestFilter = RequestFilterType;
|
public RequestFilter = RequestFilterType;
|
||||||
|
|
||||||
|
@ -46,10 +47,11 @@ export class MoviesGridComponent implements OnInit, AfterViewInit {
|
||||||
@ViewChild(MatSort) sort: MatSort;
|
@ViewChild(MatSort) sort: MatSort;
|
||||||
|
|
||||||
constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef,
|
constructor(private requestService: RequestServiceV2, private ref: ChangeDetectorRef,
|
||||||
private auth: AuthService, private storageService: StorageService,
|
private auth: AuthService, private storageService: StorageService,
|
||||||
private requestServiceV1: RequestService, private notification: NotificationService,
|
private requestServiceV1: RequestService, private notification: NotificationService,
|
||||||
private translateService: TranslateService) {
|
private translateService: TranslateService) {
|
||||||
|
|
||||||
|
this.userName = auth.claims().name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit() {
|
public ngOnInit() {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
import { Component, Inject } from '@angular/core';
|
import { Component, Inject } from '@angular/core';
|
||||||
import { MAT_BOTTOM_SHEET_DATA, MatBottomSheetRef } from '@angular/material/bottom-sheet';
|
import { MAT_BOTTOM_SHEET_DATA, MatBottomSheetRef } from '@angular/material/bottom-sheet';
|
||||||
import { RequestService } from '../../../services';
|
import { MessageService, RequestService } from '../../../services';
|
||||||
import { RequestType } from '../../../interfaces';
|
import { IRequestEngineResult, RequestType } from '../../../interfaces';
|
||||||
import { UpdateType } from '../../models/UpdateType';
|
import { UpdateType } from '../../models/UpdateType';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'request-options',
|
selector: 'request-options',
|
||||||
|
@ -13,21 +15,31 @@ export class RequestOptionsComponent {
|
||||||
public RequestType = RequestType;
|
public RequestType = RequestType;
|
||||||
|
|
||||||
constructor(@Inject(MAT_BOTTOM_SHEET_DATA) public data: any,
|
constructor(@Inject(MAT_BOTTOM_SHEET_DATA) public data: any,
|
||||||
private requestService: RequestService, private bottomSheetRef: MatBottomSheetRef<RequestOptionsComponent>) { }
|
private requestService: RequestService,
|
||||||
|
private messageService: MessageService,
|
||||||
|
private bottomSheetRef: MatBottomSheetRef<RequestOptionsComponent>,
|
||||||
|
private translate: TranslateService) { }
|
||||||
|
|
||||||
public async delete() {
|
public async delete() {
|
||||||
|
var request: Observable<IRequestEngineResult>;
|
||||||
if (this.data.type === RequestType.movie) {
|
if (this.data.type === RequestType.movie) {
|
||||||
await this.requestService.removeMovieRequestAsync(this.data.id);
|
request = this.requestService.removeMovieRequestAsync(this.data.id);
|
||||||
}
|
}
|
||||||
if (this.data.type === RequestType.tvShow) {
|
if (this.data.type === RequestType.tvShow) {
|
||||||
await this.requestService.deleteChild(this.data.id).toPromise();
|
request = this.requestService.deleteChild(this.data.id);
|
||||||
}
|
}
|
||||||
if (this.data.type === RequestType.album) {
|
if (this.data.type === RequestType.album) {
|
||||||
await this.requestService.removeAlbumRequest(this.data.id).toPromise();
|
request = this.requestService.removeAlbumRequest(this.data.id);
|
||||||
}
|
}
|
||||||
|
request.subscribe(result => {
|
||||||
this.bottomSheetRef.dismiss({type: UpdateType.Delete});
|
if (result.result) {
|
||||||
return;
|
this.messageService.send(this.translate.instant("Requests.SuccessfullyDeleted"));
|
||||||
|
this.bottomSheetRef.dismiss({type: UpdateType.Delete});
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.messageService.sendRequestEngineResultError(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async approve() {
|
public async approve() {
|
||||||
|
|
|
@ -73,8 +73,8 @@ export class RequestService extends ServiceHelpers {
|
||||||
this.http.delete(`${this.url}movie/${requestId}`, {headers: this.headers}).subscribe();
|
this.http.delete(`${this.url}movie/${requestId}`, {headers: this.headers}).subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeMovieRequestAsync(requestId: number) {
|
public removeMovieRequestAsync(requestId: number): Observable<IRequestEngineResult> {
|
||||||
return this.http.delete(`${this.url}movie/${requestId}`, {headers: this.headers}).toPromise();
|
return this.http.delete<IRequestEngineResult>(`${this.url}movie/${requestId}`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
public updateMovieRequest(request: IMovieRequests): Observable<IMovieRequests> {
|
public updateMovieRequest(request: IMovieRequests): Observable<IMovieRequests> {
|
||||||
|
@ -129,8 +129,8 @@ export class RequestService extends ServiceHelpers {
|
||||||
return this.http.post<IRequestEngineResult>(`${this.url}tv/approve`, JSON.stringify(child), {headers: this.headers});
|
return this.http.post<IRequestEngineResult>(`${this.url}tv/approve`, JSON.stringify(child), {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
public deleteChild(childId: number): Observable<boolean> {
|
public deleteChild(childId: number): Observable<IRequestEngineResult> {
|
||||||
return this.http.delete<boolean>(`${this.url}tv/child/${childId}`, {headers: this.headers});
|
return this.http.delete<IRequestEngineResult>(`${this.url}tv/child/${childId}`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
public subscribeToMovie(requestId: number): Observable<boolean> {
|
public subscribeToMovie(requestId: number): Observable<boolean> {
|
||||||
|
@ -185,7 +185,7 @@ export class RequestService extends ServiceHelpers {
|
||||||
return this.http.get<IAlbumRequest[]>(`${this.url}music/search/${search}`, {headers: this.headers});
|
return this.http.get<IAlbumRequest[]>(`${this.url}music/search/${search}`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeAlbumRequest(request: number): any {
|
public removeAlbumRequest(request: number): Observable<IRequestEngineResult> {
|
||||||
return this.http.delete(`${this.url}music/${request}`, {headers: this.headers});
|
return this.http.delete<IRequestEngineResult>(`${this.url}music/${request}`, {headers: this.headers});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,9 +113,9 @@ namespace Ombi.Controllers.V1
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpDelete("{requestId:int}")]
|
[HttpDelete("{requestId:int}")]
|
||||||
[Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")]
|
[Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")]
|
||||||
public async Task DeleteRequest(int requestId)
|
public async Task<RequestEngineResult> DeleteRequest(int requestId)
|
||||||
{
|
{
|
||||||
await _engine.RemoveAlbumRequest(requestId);
|
return await _engine.RemoveAlbumRequest(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -128,9 +128,9 @@ namespace Ombi.Controllers.V1
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpDelete("movie/{requestId:int}")]
|
[HttpDelete("movie/{requestId:int}")]
|
||||||
[Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")]
|
[Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")]
|
||||||
public async Task DeleteRequest(int requestId)
|
public async Task<RequestEngineResult> DeleteRequest(int requestId)
|
||||||
{
|
{
|
||||||
await MovieRequestEngine.RemoveMovieRequest(requestId);
|
return await MovieRequestEngine.RemoveMovieRequest(requestId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -324,7 +324,7 @@ namespace Ombi.Controllers.V1
|
||||||
/// <param name="requestId">The request identifier.</param>
|
/// <param name="requestId">The request identifier.</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpDelete("tv/{requestId:int}")]
|
[HttpDelete("tv/{requestId:int}")]
|
||||||
[Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")]
|
[Authorize(Roles = "Admin,PowerUser")]
|
||||||
public async Task DeleteTvRequest(int requestId)
|
public async Task DeleteTvRequest(int requestId)
|
||||||
{
|
{
|
||||||
await TvRequestEngine.RemoveTvRequest(requestId);
|
await TvRequestEngine.RemoveTvRequest(requestId);
|
||||||
|
@ -437,10 +437,9 @@ namespace Ombi.Controllers.V1
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")]
|
[Authorize(Roles = "Admin,PowerUser,ManageOwnRequests")]
|
||||||
[HttpDelete("tv/child/{requestId:int}")]
|
[HttpDelete("tv/child/{requestId:int}")]
|
||||||
public async Task<bool> DeleteChildRequest(int requestId)
|
public async Task<RequestEngineResult> DeleteChildRequest(int requestId)
|
||||||
{
|
{
|
||||||
await TvRequestEngine.RemoveTvChild(requestId);
|
return await TvRequestEngine.RemoveTvChild(requestId);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,7 @@
|
||||||
"Approved": "Successfully approved selected items"
|
"Approved": "Successfully approved selected items"
|
||||||
},
|
},
|
||||||
"SuccessfullyApproved": "Successfully Approved",
|
"SuccessfullyApproved": "Successfully Approved",
|
||||||
|
"SuccessfullyDeleted": "Request successfully deleted",
|
||||||
"NowAvailable": "Request is now available",
|
"NowAvailable": "Request is now available",
|
||||||
"NowUnavailable": "Request is now unavailable",
|
"NowUnavailable": "Request is now unavailable",
|
||||||
"SuccessfullyReprocessed": "Successfully Re-processed the request",
|
"SuccessfullyReprocessed": "Successfully Re-processed the request",
|
||||||
|
|
|
@ -39,7 +39,7 @@ describe("Requests Tests", () => {
|
||||||
row.optionsDelete.click();
|
row.optionsDelete.click();
|
||||||
|
|
||||||
cy.wait('@deleteRequest').then((intercept) => {
|
cy.wait('@deleteRequest').then((intercept) => {
|
||||||
expect(intercept.response.body).is.true;
|
expect(intercept.response.body.result).is.true;
|
||||||
})
|
})
|
||||||
|
|
||||||
row.title.should('not.exist');
|
row.title.should('not.exist');
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue