Need to pass the token to keep-web without it being "sticky" in the
URL bar. Using a query param accomplishes this, because keep-web
knows to strip the api_token query parameter and respond with redirect
and a cookie which the browser can use to fetch the file safely.
Also distinguish between KeepWebService (now the download service) and
KeepWebInlineService (the one that will serve content that can be
displayed inline in the browser if it is safe to do so).
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz@curii.com>
export class Config {
baseUrl: string;
keepWebServiceUrl: string;
export class Config {
baseUrl: string;
keepWebServiceUrl: string;
+ keepWebInlineServiceUrl: string;
remoteHosts: {
[key: string]: string
};
remoteHosts: {
[key: string]: string
};
config.workbench2Url = clusterConfigJSON.Services.Workbench2.ExternalURL;
config.workbenchUrl = clusterConfigJSON.Services.Workbench1.ExternalURL;
config.keepWebServiceUrl = clusterConfigJSON.Services.WebDAVDownload.ExternalURL;
config.workbench2Url = clusterConfigJSON.Services.Workbench2.ExternalURL;
config.workbenchUrl = clusterConfigJSON.Services.Workbench1.ExternalURL;
config.keepWebServiceUrl = clusterConfigJSON.Services.WebDAVDownload.ExternalURL;
+ config.keepWebInlineServiceUrl = clusterConfigJSON.Services.WebDAV.ExternalURL;
config.loginCluster = clusterConfigJSON.Login.LoginCluster;
config.clusterConfig = clusterConfigJSON;
config.apiRevision = 0;
config.loginCluster = clusterConfigJSON.Login.LoginCluster;
config.clusterConfig = clusterConfigJSON;
config.apiRevision = 0;
export const mockConfig = (config: Partial<Config>): Config => ({
baseUrl: "",
keepWebServiceUrl: "",
export const mockConfig = (config: Partial<Config>): Config => ({
baseUrl: "",
keepWebServiceUrl: "",
+ keepWebInlineServiceUrl: "",
remoteHosts: {},
rootUrl: "",
uuidPrefix: "",
remoteHosts: {},
rootUrl: "",
uuidPrefix: "",
port: '80',
protocol: 'http',
search: '',
port: '80',
protocol: 'http',
search: '',
- reload: () => {},
- replace: () => {},
- assign: () => {},
+ reload: () => { },
+ replace: () => { },
+ assign: () => { },
ancestorOrigins: [],
href: '',
};
ancestorOrigins: [],
href: '',
};
it('should redirect to page when it is present in session storage', () => {
// when
it('should redirect to page when it is present in session storage', () => {
// when
- handleRedirects(config);
+ handleRedirects("abcxyz", config);
- expect(window.location.href).toBe(`${config.keepWebServiceUrl}${redirectTo}`);
+ expect(window.location.href).toBe(`${config.keepWebServiceUrl}${redirectTo}?api_token=abcxyz`);
-});
\ No newline at end of file
if (window.location.href.indexOf(REDIRECT_TO_KEY) > -1) {
const { location: { href }, sessionStorage } = window;
const redirectUrl = href.split(`${REDIRECT_TO_KEY}=`)[1];
if (window.location.href.indexOf(REDIRECT_TO_KEY) > -1) {
const { location: { href }, sessionStorage } = window;
const redirectUrl = href.split(`${REDIRECT_TO_KEY}=`)[1];
if (sessionStorage) {
sessionStorage.setItem(REDIRECT_TO_KEY, redirectUrl);
}
}
};
if (sessionStorage) {
sessionStorage.setItem(REDIRECT_TO_KEY, redirectUrl);
}
}
};
-export const handleRedirects = (config: Config) => {
+export const handleRedirects = (token: string, config: Config) => {
const { sessionStorage } = window;
const { keepWebServiceUrl } = config;
if (sessionStorage && sessionStorage.getItem(REDIRECT_TO_KEY)) {
const redirectUrl = sessionStorage.getItem(REDIRECT_TO_KEY);
sessionStorage.removeItem(REDIRECT_TO_KEY);
const { sessionStorage } = window;
const { keepWebServiceUrl } = config;
if (sessionStorage && sessionStorage.getItem(REDIRECT_TO_KEY)) {
const redirectUrl = sessionStorage.getItem(REDIRECT_TO_KEY);
sessionStorage.removeItem(REDIRECT_TO_KEY);
- window.location.href = `${keepWebServiceUrl}${redirectUrl}`;
+ if (redirectUrl) {
+ const sep = redirectUrl.indexOf("?") > -1 ? "&" : "?";
+ window.location.href = `${keepWebServiceUrl}${redirectUrl}${sep}api_token=${token}`;
+ }
-};
\ No newline at end of file
configFromDD = {
baseUrl: normalizeURLPath(dd.baseUrl),
keepWebServiceUrl: dd.keepWebServiceUrl,
configFromDD = {
baseUrl: normalizeURLPath(dd.baseUrl),
keepWebServiceUrl: dd.keepWebServiceUrl,
+ keepWebInlineServiceUrl: dd.keepWebInlineServiceUrl,
remoteHosts: dd.remoteHosts,
rootUrl: dd.rootUrl,
uuidPrefix: dd.uuidPrefix,
remoteHosts: dd.remoteHosts,
rootUrl: dd.rootUrl,
uuidPrefix: dd.uuidPrefix,
const state = store.getState();
if (state.auth && state.auth.apiToken) {
const state = store.getState();
if (state.auth && state.auth.apiToken) {
- handleRedirects(config);
+ handleRedirects(state.auth.apiToken, config);
const file = getNodeValue(resource.uuid)(state.collectionPanelFiles);
if (file) {
return {
const file = getNodeValue(resource.uuid)(state.collectionPanelFiles);
if (file) {
return {
+ href: file.url.replace(state.auth.config.keepWebServiceUrl, state.auth.config.keepWebInlineServiceUrl),
kind: 'file',
currentCollectionUuid
};
}
} else {
kind: 'file',
currentCollectionUuid
};
}
} else {
};
export const CollectionFileViewerAction = connect(mapStateToProps)(FileViewerAction);
};
export const CollectionFileViewerAction = connect(mapStateToProps)(FileViewerAction);
return props.href || props.kind === 'files'
? <a
style={{ textDecoration: 'none' }}
return props.href || props.kind === 'files'
? <a
style={{ textDecoration: 'none' }}
- href={props.kind === 'files' ? undefined : `${props.href}?disposition=attachment`}
+ href={props.kind === 'files' ? undefined : props.href}
onClick={props.onClick}
{...downloadProps}>
<ListItem button onClick={() => props.kind === 'files' ? createZip(props.href, props.download) : undefined}>
onClick={props.onClick}
{...downloadProps}>
<ListItem button onClick={() => props.kind === 'files' ? createZip(props.href, props.download) : undefined}>
-};
\ No newline at end of file
const file = getNodeValue(resource.uuid)(state.collectionPanelFiles);
if (file) {
return {
const file = getNodeValue(resource.uuid)(state.collectionPanelFiles);
if (file) {
return {
- href: sanitizeToken(file.url, false),
+ href: sanitizeToken(file.url, true),
kind: 'file',
currentCollectionUuid
};
kind: 'file',
currentCollectionUuid
};
} else {
const files = filterCollectionFilesBySelection(state.collectionPanelFiles, true);
return {
} else {
const files = filterCollectionFilesBySelection(state.collectionPanelFiles, true);
return {
- href: files.map(file => sanitizeToken(file.url, false)),
+ href: files.map(file => sanitizeToken(file.url, true)),
kind: 'files',
currentCollectionUuid
};
kind: 'files',
currentCollectionUuid
};
return props.href
? <a
style={{ textDecoration: 'none' }}
return props.href
? <a
style={{ textDecoration: 'none' }}
- href={sanitizeToken(props.href, false)}
+ href={sanitizeToken(props.href, true)}
target="_blank"
onClick={props.onClick}>
<ListItem button>
target="_blank"
onClick={props.onClick}>
<ListItem button>
- <ListItemIcon>
- <OpenIcon />
- </ListItemIcon>
+ <ListItemIcon>
+ <OpenIcon />
+ </ListItemIcon>
<ListItemText>
Open in new tab
<ListItemText>
Open in new tab
-};
\ No newline at end of file
const [prefix, suffix] = href.split('/t=');
const [token, ...rest] = suffix.split('/');
const [prefix, suffix] = href.split('/t=');
const [token, ...rest] = suffix.split('/');
- return `${[prefix, ...rest].join('/')}${tokenAsQueryParam ? `?api_token=${token}` : ''}`;
+ const sep = href.indexOf("?") > -1 ? "&" : "?";
+
+ return `${[prefix, ...rest].join('/')}${tokenAsQueryParam ? `${sep}api_token=${token}` : ''}`;
};
export const getClipboardUrl = (href: string): string => {
};
export const getClipboardUrl = (href: string): string => {
const url = sanitizeToken(href, false);
return `${origin}?redirectTo=${url}`;
const url = sanitizeToken(href, false);
return `${origin}?redirectTo=${url}`;
-};
\ No newline at end of file