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 { import {
getAccessTokens, getAccessTokens,
selectCurrentAuthServices,
} from '../../../src/state/selectors/auth'; } from '../../../src/state/selectors/auth';
describe('getAccessTokens', () => { describe('getAccessTokens', () => {
...@@ -16,3 +20,178 @@ describe('getAccessTokens', () => { ...@@ -16,3 +20,178 @@ describe('getAccessTokens', () => {
expect(accessTokens).toEqual(state.accessTokens); 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 { ...@@ -10,7 +10,6 @@ import {
getPreviousCanvasGrouping, getPreviousCanvasGrouping,
getCanvas, getCanvas,
getCanvasLabel, getCanvasLabel,
selectCanvasAuthService,
selectInfoResponse, selectInfoResponse,
getVisibleCanvasNonTiledResources, getVisibleCanvasNonTiledResources,
getVisibleCanvasIds, getVisibleCanvasIds,
...@@ -250,150 +249,6 @@ describe('getCanvasLabel', () => { ...@@ -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', () => { describe('selectInfoResponse', () => {
it('returns in the info response for the first canvas resource', () => { it('returns in the info response for the first canvas resource', () => {
const resource = { some: 'resource' }; const resource = { some: 'resource' };
......
...@@ -5,10 +5,9 @@ import { Utils } from 'manifesto.js/dist-esmodule/Utils'; ...@@ -5,10 +5,9 @@ import { Utils } from 'manifesto.js/dist-esmodule/Utils';
import { withPlugins } from '../extend/withPlugins'; import { withPlugins } from '../extend/withPlugins';
import * as actions from '../state/actions'; import * as actions from '../state/actions';
import { import {
getCurrentCanvas,
getAuth, getAuth,
isInteractiveAuth, getAuthProfiles,
selectCanvasAuthService, selectCurrentAuthServices,
getAccessTokens, getAccessTokens,
} from '../state/selectors'; } from '../state/selectors';
import { IIIFAuthentication } from '../components/IIIFAuthentication'; import { IIIFAuthentication } from '../components/IIIFAuthentication';
...@@ -19,8 +18,10 @@ import { IIIFAuthentication } from '../components/IIIFAuthentication'; ...@@ -19,8 +18,10 @@ import { IIIFAuthentication } from '../components/IIIFAuthentication';
* @private * @private
*/ */
const mapStateToProps = (state, { windowId }) => { const mapStateToProps = (state, { windowId }) => {
const canvasId = (getCurrentCanvas(state, { windowId }) || {}).id; const services = selectCurrentAuthServices(state, { windowId });
const service = selectCanvasAuthService(state, { canvasId, windowId });
// TODO: get the most actionable auth service...
const service = services[0];
const accessTokenService = service && ( const accessTokenService = service && (
Utils.getService(service, 'http://iiif.io/api/auth/1/token') Utils.getService(service, 'http://iiif.io/api/auth/1/token')
...@@ -31,12 +32,10 @@ const mapStateToProps = (state, { windowId }) => { ...@@ -31,12 +32,10 @@ const mapStateToProps = (state, { windowId }) => {
|| Utils.getService(service, 'http://iiif.io/api/auth/0/logout') || 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 authStatus = service && authStatuses[service.id];
const accessTokens = authStatus && accessTokenService && getAccessTokens(state); const accessTokens = getAccessTokens(state);
const accessTokenStatus = accessTokens && Object.values(accessTokens).find( const accessTokenStatus = accessTokenService && accessTokens[accessTokenService.id];
e => e.id === accessTokenService.id,
);
let status; let status;
...@@ -45,18 +44,20 @@ const mapStateToProps = (state, { windowId }) => { ...@@ -45,18 +44,20 @@ const mapStateToProps = (state, { windowId }) => {
} else if (authStatus.ok) { } else if (authStatus.ok) {
status = 'ok'; status = 'ok';
} else if (authStatus.isFetching) { } else if (authStatus.isFetching) {
if (authStatus.windowId === windowId) { if (authStatus.windowId === windowId) status = 'cookie';
status = 'cookie';
}
} else if (accessTokenStatus && accessTokenStatus.isFetching) { } else if (accessTokenStatus && accessTokenStatus.isFetching) {
if (authStatus.windowId === windowId) { if (authStatus.windowId === windowId) status = 'token';
status = 'token';
}
} else { } else {
status = 'failed'; 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 { return {
accessTokenServiceId: accessTokenService && accessTokenService.id, accessTokenServiceId: accessTokenService && accessTokenService.id,
...@@ -69,7 +70,7 @@ const mapStateToProps = (state, { windowId }) => { ...@@ -69,7 +70,7 @@ const mapStateToProps = (state, { windowId }) => {
isInteractive, isInteractive,
label: service && service.getLabel()[0].value, label: service && service.getLabel()[0].value,
logoutServiceId: logoutService && logoutService.id, logoutServiceId: logoutService && logoutService.id,
profile: service && service.getProfile(), profile,
status, status,
}; };
}; };
......
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { Utils } from 'manifesto.js/dist-esmodule/Utils'; import { Utils } from 'manifesto.js/dist-esmodule/Utils';
import flatten from 'lodash/flatten';
import MiradorCanvas from '../../lib/MiradorCanvas'; import MiradorCanvas from '../../lib/MiradorCanvas';
import { miradorSlice } from './utils'; import { miradorSlice } from './utils';
import { getConfig } from './config'; 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 || {}; export const getAccessTokens = state => miradorSlice(state).accessTokens || {};
...@@ -11,72 +19,65 @@ 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 getAuth = state => miradorSlice(state).auth || {};
export const selectCanvasAuthService = createSelector( export const selectCurrentAuthServices = createSelector(
[ [
selectInfoResponse, getVisibleCanvases,
getCanvas, selectInfoResponses,
getConfig, getAuthProfiles,
getAuth, getAuth,
(state, { iiifResources }) => iiifResources,
], ],
(infoResponse, canvas, { auth: { serviceProfiles = [] } }, auth) => { (canvases, infoResponses = {}, serviceProfiles, auth, iiifResources) => {
let iiifResource; let currentAuthResources = iiifResources;
iiifResource = infoResponse && infoResponse.json && { ...infoResponse.json, options: {} };
if (!iiifResource && canvas) { if (!currentAuthResources && canvases) {
const miradorCanvas = new MiradorCanvas(canvas); currentAuthResources = flatten(canvases.map(c => {
const [image] = miradorCanvas.iiifImageResources; 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; let lastAttemptedService;
const services = Utils.getServices(resource);
for (const profile of orderedAuthServiceProfiles) { for (const authProfile of serviceProfiles) {
const services = getServices(iiifResource, profile); const profiledAuthServices = services.filter(
for (const service of services) { p => authProfile.profile === p.getProfile(),
if (!auth[service.id]) return service; );
for (const service of profiledAuthServices) {
lastAttemptedService = service; 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 */ return lastAttemptedService;
function getServices(resource, profile) { });
const services = Utils.getServices(resource);
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" */ return h;
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),
);
}, },
); );
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment