Skip to content
Snippets Groups Projects
Commit d0ba3be3 authored by Chris Beer's avatar Chris Beer
Browse files

Refactor selectCanvasAuthService => selectCurrentAuthServices to support...

Refactor selectCanvasAuthService => selectCurrentAuthServices to support different auth across canvases
parent 5bc07298
No related branches found
No related tags found
No related merge requests found
import manifestFixture001 from '../../fixtures/version-2/001.json';
import manifestFixture019 from '../../fixtures/version-2/019.json';
import settings from '../../../src/config/settings';
import {
getAccessTokens,
selectCurrentAuthServices,
} from '../../../src/state/selectors/auth';
describe('getAccessTokens', () => {
......@@ -16,3 +20,178 @@ describe('getAccessTokens', () => {
expect(accessTokens).toEqual(state.accessTokens);
});
});
describe('selectCurrentAuthServices', () => {
const resource = {
service: [
{
'@id': 'external',
profile: 'http://iiif.io/api/auth/1/external',
},
{
'@id': 'login',
profile: 'http://iiif.io/api/auth/1/login',
},
],
};
const externalOnly = {
service: [
{
'@id': 'external',
profile: 'http://iiif.io/api/auth/1/external',
},
],
};
const state = {
auth: {},
config: { auth: settings.auth },
infoResponses: {
'https://iiif.bodleian.ox.ac.uk/iiif/image/9cca8fdd-4a61-4429-8ac1-f648764b4d6d': {
json: resource,
},
'https://stacks.stanford.edu/image/iiif/hg676jb4964%2F0380_796-44': {
json: externalOnly,
},
},
manifests: {
a: {
json: manifestFixture001,
},
b: {
json: manifestFixture019,
},
},
windows: {
noCanvas: {
manifestId: 'a',
},
w: {
manifestId: 'a',
visibleCanvases: [
'https://iiif.bodleian.ox.ac.uk/iiif/canvas/9cca8fdd-4a61-4429-8ac1-f648764b4d6d.json',
],
},
x: {
manifestId: 'b',
visibleCanvases: [
'http://iiif.io/api/presentation/2.0/example/fixtures/canvas/24/c1.json',
],
},
y: {
manifestId: 'b',
visibleCanvases: [
'https://purl.stanford.edu/fr426cg9537/iiif/canvas/fr426cg9537_1',
],
},
},
};
it('returns undefined if there is no current canvas', () => {
expect(selectCurrentAuthServices({ config: { auth: settings.auth }, manifests: {} }, { windowId: 'noCanvas' })[0]).toBeUndefined();
});
it('returns the next auth service to try', () => {
expect(selectCurrentAuthServices(state, { windowId: 'w' })[0].id).toEqual('external');
});
it('returns the service if the next auth service is interactive', () => {
const auth = { external: { isFetching: false, ok: false } };
expect(selectCurrentAuthServices({ ...state, auth }, { windowId: 'w' })[0].id).toEqual('login');
});
it('returns the last attempted auth service if all of them have been tried', () => {
const auth = {
external: { isFetching: false, ok: false },
login: { isFetching: false, ok: false },
};
expect(selectCurrentAuthServices({ ...state, auth }, { windowId: 'w' })[0].id).toEqual('login');
expect(selectCurrentAuthServices({ ...state, auth }, { windowId: 'x' })[0].id).toEqual('external');
expect(selectCurrentAuthServices({ ...state, auth }, { windowId: 'y' })[0]).toBeUndefined();
});
describe('proscribed order', () => {
let auth = {};
const orderedState = {
config: { auth: settings.auth },
infoResponses: {
'https://iiif.bodleian.ox.ac.uk/iiif/image/9cca8fdd-4a61-4429-8ac1-f648764b4d6d': {
json: {
service: [
{
'@id': 'external',
profile: 'http://iiif.io/api/auth/1/external',
},
{
'@id': 'kiosk',
profile: 'http://iiif.io/api/auth/1/kiosk',
},
{
'@id': 'clickthrough',
profile: 'http://iiif.io/api/auth/1/clickthrough',
},
{
'@id': 'login',
profile: 'http://iiif.io/api/auth/1/login',
},
{
'@id': 'login2',
profile: 'http://iiif.io/api/auth/1/login',
},
],
},
},
},
manifests: {
a: {
json: manifestFixture001,
},
},
windows: {
w: {
manifestId: 'a',
visibleCanvases: [
'https://iiif.bodleian.ox.ac.uk/iiif/canvas/9cca8fdd-4a61-4429-8ac1-f648764b4d6d.json',
],
},
},
};
it('returns external first', () => {
auth = {};
expect(selectCurrentAuthServices({ ...orderedState, auth }, { windowId: 'w' })[0].id).toEqual('external');
});
it('returns kiosk next', () => {
auth = { external: { isFetching: false, ok: false } };
expect(selectCurrentAuthServices({ ...orderedState, auth }, { windowId: 'w' })[0].id).toEqual('kiosk');
});
it('returns clickthrough next', () => {
auth = {
external: { isFetching: false, ok: false },
kiosk: { isFetching: false, ok: false },
};
expect(selectCurrentAuthServices({ ...orderedState, auth }, { windowId: 'w' })[0].id).toEqual('clickthrough');
});
it('returns logins last', () => {
auth = {
clickthrough: { isFetching: false, ok: false },
external: { isFetching: false, ok: false },
kiosk: { isFetching: false, ok: false },
};
expect(selectCurrentAuthServices({ ...orderedState, auth }, { windowId: 'w' })[0].id).toEqual('login');
});
it('returns services within a given type using the order from the manifest', () => {
auth = {
clickthrough: { isFetching: false, ok: false },
external: { isFetching: false, ok: false },
kiosk: { isFetching: false, ok: false },
login: { isFetching: false, ok: false },
};
expect(selectCurrentAuthServices({ ...orderedState, auth }, { windowId: 'w' })[0].id).toEqual('login2');
});
});
});
......@@ -10,7 +10,6 @@ import {
getPreviousCanvasGrouping,
getCanvas,
getCanvasLabel,
selectCanvasAuthService,
selectInfoResponse,
getVisibleCanvasNonTiledResources,
getVisibleCanvasIds,
......@@ -250,150 +249,6 @@ describe('getCanvasLabel', () => {
});
});
describe('selectCanvasAuthService', () => {
const resource = {
service: [
{
'@id': 'external',
profile: 'http://iiif.io/api/auth/1/external',
},
{
'@id': 'login',
profile: 'http://iiif.io/api/auth/1/login',
},
],
};
const externalOnly = {
service: [
{
'@id': 'external',
profile: 'http://iiif.io/api/auth/1/external',
},
],
};
const state = {
auth: {},
config: { auth: settings.auth },
infoResponses: {
'https://iiif.bodleian.ox.ac.uk/iiif/image/9cca8fdd-4a61-4429-8ac1-f648764b4d6d': {
json: resource,
},
'https://stacks.stanford.edu/image/iiif/hg676jb4964%2F0380_796-44': {
json: externalOnly,
},
},
manifests: {
a: {
json: manifestFixture001,
},
b: {
json: manifestFixture019,
},
},
};
it('returns undefined if there is no current canvas', () => {
expect(selectCanvasAuthService({ config: { auth: settings.auth }, manifests: {} }, { manifestId: 'a' })).toBeUndefined();
});
it('returns the next auth service to try', () => {
expect(selectCanvasAuthService(state, { canvasId: 'https://iiif.bodleian.ox.ac.uk/iiif/canvas/9cca8fdd-4a61-4429-8ac1-f648764b4d6d.json', manifestId: 'a' }).id).toEqual('external');
});
it('returns the service if the next auth service is interactive', () => {
const auth = { external: { isFetching: false, ok: false } };
expect(selectCanvasAuthService({ ...state, auth }, { canvasId: 'https://iiif.bodleian.ox.ac.uk/iiif/canvas/9cca8fdd-4a61-4429-8ac1-f648764b4d6d.json', manifestId: 'a' }).id).toEqual('login');
});
it('returns the last attempted auth service if all of them have been tried', () => {
const auth = {
external: { isFetching: false, ok: false },
login: { isFetching: false, ok: false },
};
expect(selectCanvasAuthService({ ...state, auth }, { canvasId: 'https://iiif.bodleian.ox.ac.uk/iiif/canvas/9cca8fdd-4a61-4429-8ac1-f648764b4d6d.json', manifestId: 'a' }).id).toEqual('login');
expect(selectCanvasAuthService({ ...state, auth }, { canvasId: 'http://iiif.io/api/presentation/2.0/example/fixtures/canvas/24/c1.json', manifestId: 'b' }).id).toEqual('external');
expect(selectCanvasAuthService({ ...state, auth }, { canvasId: 'https://purl.stanford.edu/fr426cg9537/iiif/canvas/fr426cg9537_1', manifestId: 'b' })).toBeUndefined();
});
describe('proscribed order', () => {
let auth = {};
const orderedState = {
config: { auth: settings.auth },
infoResponses: {
'https://iiif.bodleian.ox.ac.uk/iiif/image/9cca8fdd-4a61-4429-8ac1-f648764b4d6d': {
json: {
service: [
{
'@id': 'external',
profile: 'http://iiif.io/api/auth/1/external',
},
{
'@id': 'kiosk',
profile: 'http://iiif.io/api/auth/1/kiosk',
},
{
'@id': 'clickthrough',
profile: 'http://iiif.io/api/auth/1/clickthrough',
},
{
'@id': 'login',
profile: 'http://iiif.io/api/auth/1/login',
},
{
'@id': 'login2',
profile: 'http://iiif.io/api/auth/1/login',
},
],
},
},
},
manifests: {
a: {
json: manifestFixture001,
},
},
};
it('returns external first', () => {
auth = {};
expect(selectCanvasAuthService({ ...orderedState, auth }, { canvasId: 'https://iiif.bodleian.ox.ac.uk/iiif/canvas/9cca8fdd-4a61-4429-8ac1-f648764b4d6d.json', manifestId: 'a' }).id).toEqual('external');
});
it('returns kiosk next', () => {
auth = { external: { isFetching: false, ok: false } };
expect(selectCanvasAuthService({ ...orderedState, auth }, { canvasId: 'https://iiif.bodleian.ox.ac.uk/iiif/canvas/9cca8fdd-4a61-4429-8ac1-f648764b4d6d.json', manifestId: 'a' }).id).toEqual('kiosk');
});
it('returns clickthrough next', () => {
auth = {
external: { isFetching: false, ok: false },
kiosk: { isFetching: false, ok: false },
};
expect(selectCanvasAuthService({ ...orderedState, auth }, { canvasId: 'https://iiif.bodleian.ox.ac.uk/iiif/canvas/9cca8fdd-4a61-4429-8ac1-f648764b4d6d.json', manifestId: 'a' }).id).toEqual('clickthrough');
});
it('returns logins last', () => {
auth = {
clickthrough: { isFetching: false, ok: false },
external: { isFetching: false, ok: false },
kiosk: { isFetching: false, ok: false },
};
expect(selectCanvasAuthService({ ...orderedState, auth }, { canvasId: 'https://iiif.bodleian.ox.ac.uk/iiif/canvas/9cca8fdd-4a61-4429-8ac1-f648764b4d6d.json', manifestId: 'a' }).id).toEqual('login');
});
it('returns services within a given type using the order from the manifest', () => {
auth = {
clickthrough: { isFetching: false, ok: false },
external: { isFetching: false, ok: false },
kiosk: { isFetching: false, ok: false },
login: { isFetching: false, ok: false },
};
expect(selectCanvasAuthService({ ...orderedState, auth }, { canvasId: 'https://iiif.bodleian.ox.ac.uk/iiif/canvas/9cca8fdd-4a61-4429-8ac1-f648764b4d6d.json', manifestId: 'a' }).id).toEqual('login2');
});
});
});
describe('selectInfoResponse', () => {
it('returns in the info response for the first canvas resource', () => {
const resource = { some: 'resource' };
......
......@@ -5,10 +5,9 @@ import { Utils } from 'manifesto.js/dist-esmodule/Utils';
import { withPlugins } from '../extend/withPlugins';
import * as actions from '../state/actions';
import {
getCurrentCanvas,
getAuth,
isInteractiveAuth,
selectCanvasAuthService,
getAuthProfiles,
selectCurrentAuthServices,
getAccessTokens,
} from '../state/selectors';
import { IIIFAuthentication } from '../components/IIIFAuthentication';
......@@ -19,8 +18,10 @@ import { IIIFAuthentication } from '../components/IIIFAuthentication';
* @private
*/
const mapStateToProps = (state, { windowId }) => {
const canvasId = (getCurrentCanvas(state, { windowId }) || {}).id;
const service = selectCanvasAuthService(state, { canvasId, windowId });
const services = selectCurrentAuthServices(state, { windowId });
// TODO: get the most actionable auth service...
const service = services[0];
const accessTokenService = service && (
Utils.getService(service, 'http://iiif.io/api/auth/1/token')
......@@ -31,12 +32,10 @@ const mapStateToProps = (state, { windowId }) => {
|| Utils.getService(service, 'http://iiif.io/api/auth/0/logout')
);
const authStatuses = getAuth(state) || {};
const authStatuses = getAuth(state);
const authStatus = service && authStatuses[service.id];
const accessTokens = authStatus && accessTokenService && getAccessTokens(state);
const accessTokenStatus = accessTokens && Object.values(accessTokens).find(
e => e.id === accessTokenService.id,
);
const accessTokens = getAccessTokens(state);
const accessTokenStatus = accessTokenService && accessTokens[accessTokenService.id];
let status;
......@@ -45,18 +44,20 @@ const mapStateToProps = (state, { windowId }) => {
} else if (authStatus.ok) {
status = 'ok';
} else if (authStatus.isFetching) {
if (authStatus.windowId === windowId) {
status = 'cookie';
}
if (authStatus.windowId === windowId) status = 'cookie';
} else if (accessTokenStatus && accessTokenStatus.isFetching) {
if (authStatus.windowId === windowId) {
status = 'token';
}
if (authStatus.windowId === windowId) status = 'token';
} else {
status = 'failed';
}
const isInteractive = isInteractiveAuth(state, { canvasId, windowId });
const authProfiles = getAuthProfiles(state);
const profile = service && service.getProfile();
const isInteractive = authProfiles.some(
config => config.profile === profile && !(config.external || config.kiosk),
);
return {
accessTokenServiceId: accessTokenService && accessTokenService.id,
......@@ -69,7 +70,7 @@ const mapStateToProps = (state, { windowId }) => {
isInteractive,
label: service && service.getLabel()[0].value,
logoutServiceId: logoutService && logoutService.id,
profile: service && service.getProfile(),
profile,
status,
};
};
......
import { createSelector } from 'reselect';
import { Utils } from 'manifesto.js/dist-esmodule/Utils';
import flatten from 'lodash/flatten';
import MiradorCanvas from '../../lib/MiradorCanvas';
import { miradorSlice } from './utils';
import { getConfig } from './config';
import { selectInfoResponse, getCanvas } from './canvases';
import { getVisibleCanvases, selectInfoResponses } from './canvases';
export const getAuthProfiles = createSelector(
[
getConfig,
],
({ auth: { serviceProfiles = [] } }) => serviceProfiles,
);
/** */
export const getAccessTokens = state => miradorSlice(state).accessTokens || {};
......@@ -11,72 +19,65 @@ export const getAccessTokens = state => miradorSlice(state).accessTokens || {};
/** */
export const getAuth = state => miradorSlice(state).auth || {};
export const selectCanvasAuthService = createSelector(
export const selectCurrentAuthServices = createSelector(
[
selectInfoResponse,
getCanvas,
getConfig,
getVisibleCanvases,
selectInfoResponses,
getAuthProfiles,
getAuth,
(state, { iiifResources }) => iiifResources,
],
(infoResponse, canvas, { auth: { serviceProfiles = [] } }, auth) => {
let iiifResource;
iiifResource = infoResponse && infoResponse.json && { ...infoResponse.json, options: {} };
(canvases, infoResponses = {}, serviceProfiles, auth, iiifResources) => {
let currentAuthResources = iiifResources;
if (!iiifResource && canvas) {
const miradorCanvas = new MiradorCanvas(canvas);
const [image] = miradorCanvas.iiifImageResources;
if (!currentAuthResources && canvases) {
currentAuthResources = flatten(canvases.map(c => {
const miradorCanvas = new MiradorCanvas(c);
const images = miradorCanvas.iiifImageResources;
iiifResource = image && image.getServices()[0];
return images.map(i => {
const iiifImageService = i.getServices()[0];
const infoResponse = infoResponses[iiifImageService.id];
if (infoResponse && infoResponse.json) {
return { ...infoResponse.json, options: {} };
}
if (!iiifResource) return undefined;
return iiifImageService;
});
}));
}
const orderedAuthServiceProfiles = serviceProfiles.map(p => p.profile);
if (!currentAuthResources) return [];
if (currentAuthResources.length === 0) return [];
const currentAuthServices = currentAuthResources.map(resource => {
let lastAttemptedService;
const services = Utils.getServices(resource);
for (const profile of orderedAuthServiceProfiles) {
const services = getServices(iiifResource, profile);
for (const service of services) {
if (!auth[service.id]) return service;
for (const authProfile of serviceProfiles) {
const profiledAuthServices = services.filter(
p => authProfile.profile === p.getProfile(),
);
for (const service of profiledAuthServices) {
lastAttemptedService = service;
if (auth[service.id].isFetching || auth[service.id].ok) return service;
if (!auth[service.id] || auth[service.id].isFetching || auth[service.id].ok) {
return service;
}
}
return lastAttemptedService;
},
);
/** */
export function selectAuthStatus({ auth }, service) {
if (!service) return null;
if (!auth[service.id]) return null;
if (auth[service.id].isFetching) return 'fetching';
if (auth[service.id].ok) return 'ok';
return 'failed';
}
/** Get all the services that match a profile */
function getServices(resource, profile) {
const services = Utils.getServices(resource);
return lastAttemptedService;
});
return services.filter(service => service.getProfile() === profile);
return Object.values(currentAuthServices.reduce((h, service) => {
if (service && !h[service.id]) {
h[service.id] = service; // eslint-disable-line no-param-reassign
}
/** check if the current auth service is "interactive" */
export const isInteractiveAuth = createSelector(
[
selectCanvasAuthService,
getConfig,
],
(service, { auth: { serviceProfiles = [] } }) => {
const profile = service && service.getProfile();
return serviceProfiles.some(
config => config.profile === profile && !(config.external || config.kiosk),
);
return h;
}, {}));
},
);
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment