94273: Make isAuthorized depend on the target object

This ensures that a successful ItemDataService.setWithdrawn call invalidates all related authorizations
This commit is contained in:
Yury Bondarenko
2022-09-08 13:27:34 +02:00
parent 7c13db2f89
commit 293ba8408e
2 changed files with 64 additions and 6 deletions

View File

@@ -3,7 +3,7 @@ import { SiteDataService } from '../site-data.service';
import { AuthService } from '../../auth/auth.service'; import { AuthService } from '../../auth/auth.service';
import { Site } from '../../shared/site.model'; import { Site } from '../../shared/site.model';
import { EPerson } from '../../eperson/models/eperson.model'; import { EPerson } from '../../eperson/models/eperson.model';
import { of as observableOf } from 'rxjs'; import { of as observableOf, combineLatest as observableCombineLatest, Observable } from 'rxjs';
import { FeatureID } from './feature-id'; import { FeatureID } from './feature-id';
import { hasValue } from '../../../shared/empty.util'; import { hasValue } from '../../../shared/empty.util';
import { RequestParam } from '../../cache/models/request-param.model'; import { RequestParam } from '../../cache/models/request-param.model';
@@ -17,6 +17,7 @@ describe('AuthorizationDataService', () => {
let service: AuthorizationDataService; let service: AuthorizationDataService;
let siteService: SiteDataService; let siteService: SiteDataService;
let authService: AuthService; let authService: AuthService;
let objectCache;
let site: Site; let site: Site;
let ePerson: EPerson; let ePerson: EPerson;
@@ -43,7 +44,11 @@ describe('AuthorizationDataService', () => {
isAuthenticated: () => observableOf(true), isAuthenticated: () => observableOf(true),
getAuthenticatedUserFromStore: () => observableOf(ePerson) getAuthenticatedUserFromStore: () => observableOf(ePerson)
} as AuthService; } as AuthService;
service = new AuthorizationDataService(requestService, undefined, undefined, undefined, undefined, undefined, undefined, undefined, authService, siteService); objectCache = jasmine.createSpyObj('objectCache', {
addDependency: undefined,
removeDependents: undefined,
});
service = new AuthorizationDataService(requestService, undefined, undefined, objectCache, undefined, undefined, undefined, undefined, authService, siteService);
} }
beforeEach(() => { beforeEach(() => {
@@ -110,6 +115,43 @@ describe('AuthorizationDataService', () => {
expect(service.searchBy).toHaveBeenCalledWith('object', createExpected(objectUrl, ePersonUuid, FeatureID.LoginOnBehalfOf), true, true); expect(service.searchBy).toHaveBeenCalledWith('object', createExpected(objectUrl, ePersonUuid, FeatureID.LoginOnBehalfOf), true, true);
}); });
}); });
describe('dependencies', () => {
let addDependencySpy;
beforeEach(() => {
(service.searchBy as any).and.returnValue(observableOf('searchBy RD$'));
addDependencySpy = spyOn(service as any, 'addDependency');
});
it('should add a dependency on the objectUrl', (done) => {
addDependencySpy.and.callFake((href$: Observable<string>, dependsOn$: Observable<string>) => {
observableCombineLatest([href$, dependsOn$]).subscribe(([href, dependsOn]) => {
expect(href).toBe('searchBy RD$');
expect(dependsOn).toBe('object-href');
});
});
service.searchByObject(FeatureID.AdministratorOf, 'object-href').subscribe(() => {
expect(addDependencySpy).toHaveBeenCalled();
done();
});
});
it('should add a dependency on the Site object if no objectUrl is given', (done) => {
addDependencySpy.and.callFake((object$: Observable<any>, dependsOn$: Observable<string>) => {
observableCombineLatest([object$, dependsOn$]).subscribe(([object, dependsOn]) => {
expect(object).toBe('searchBy RD$');
expect(dependsOn).toBe('test-site-href');
});
});
service.searchByObject(FeatureID.AdministratorOf).subscribe(() => {
expect(addDependencySpy).toHaveBeenCalled();
done();
});
});
});
}); });
describe('isAuthorized', () => { describe('isAuthorized', () => {

View File

@@ -18,10 +18,10 @@ import { followLink, FollowLinkConfig } from '../../../shared/utils/follow-link-
import { RemoteData } from '../remote-data'; import { RemoteData } from '../remote-data';
import { PaginatedList } from '../paginated-list.model'; import { PaginatedList } from '../paginated-list.model';
import { catchError, map, switchMap } from 'rxjs/operators'; import { catchError, map, switchMap } from 'rxjs/operators';
import { hasValue, isNotEmpty } from '../../../shared/empty.util'; import { hasNoValue, hasValue, isNotEmpty } from '../../../shared/empty.util';
import { RequestParam } from '../../cache/models/request-param.model'; import { RequestParam } from '../../cache/models/request-param.model';
import { AuthorizationSearchParams } from './authorization-search-params'; import { AuthorizationSearchParams } from './authorization-search-params';
import { addSiteObjectUrlIfEmpty, oneAuthorizationMatchesFeature } from './authorization-utils'; import { oneAuthorizationMatchesFeature } from './authorization-utils';
import { FeatureID } from './feature-id'; import { FeatureID } from './feature-id';
import { getFirstCompletedRemoteData } from '../../shared/operators'; import { getFirstCompletedRemoteData } from '../../shared/operators';
import { CoreState } from '../../core-state.model'; import { CoreState } from '../../core-state.model';
@@ -102,12 +102,28 @@ export class AuthorizationDataService extends DataService<Authorization> {
* {@link HALLink}s should be automatically resolved * {@link HALLink}s should be automatically resolved
*/ */
searchByObject(featureId?: FeatureID, objectUrl?: string, ePersonUuid?: string, options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<Authorization>[]): Observable<RemoteData<PaginatedList<Authorization>>> { searchByObject(featureId?: FeatureID, objectUrl?: string, ePersonUuid?: string, options: FindListOptions = {}, useCachedVersionIfAvailable = true, reRequestOnStale = true, ...linksToFollow: FollowLinkConfig<Authorization>[]): Observable<RemoteData<PaginatedList<Authorization>>> {
return observableOf(new AuthorizationSearchParams(objectUrl, ePersonUuid, featureId)).pipe( const objectUrl$ = observableOf(objectUrl).pipe(
addSiteObjectUrlIfEmpty(this.siteService), switchMap((url) => {
if (hasNoValue(url)) {
return this.siteService.find().pipe(
map((site) => site.self)
);
} else {
return observableOf(url);
}
}),
);
const out$ = objectUrl$.pipe(
map((url: string) => new AuthorizationSearchParams(url, ePersonUuid, featureId)),
switchMap((params: AuthorizationSearchParams) => { switchMap((params: AuthorizationSearchParams) => {
return this.searchBy(this.searchByObjectPath, this.createSearchOptions(params.objectUrl, options, params.ePersonUuid, params.featureId), useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow); return this.searchBy(this.searchByObjectPath, this.createSearchOptions(params.objectUrl, options, params.ePersonUuid, params.featureId), useCachedVersionIfAvailable, reRequestOnStale, ...linksToFollow);
}) })
); );
this.addDependency(out$, objectUrl$);
return out$;
} }
/** /**