Skip to content

Commit

Permalink
Disable support for cross-origin images without CORS
Browse files Browse the repository at this point in the history
Resolves #351
  • Loading branch information
mzur committed May 28, 2021
1 parent 63446df commit 7c9e380
Show file tree
Hide file tree
Showing 17 changed files with 85 additions and 58 deletions.
2 changes: 1 addition & 1 deletion public/assets/scripts/main.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion public/assets/styles/main.css

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions public/mix-manifest.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"/assets/scripts/main.js": "/assets/scripts/main.js?id=eb6371e87c99da80e2c0",
"/assets/styles/main.css": "/assets/styles/main.css?id=85e42250160e24febb38"
"/assets/scripts/main.js": "/assets/scripts/main.js?id=7beb25015f37ef585a11",
"/assets/styles/main.css": "/assets/styles/main.css?id=b8faac2691fd02d2c03d"
}
12 changes: 11 additions & 1 deletion resources/assets/js/annotations/annotatorContainer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Sidebar from '../core/components/sidebar';
import SidebarTab from '../core/components/sidebarTab';
import UserFilter from './models/UserAnnotationFilter';
import VolumeImageAreaApi from './api/volumes';
import {CrossOriginError} from './stores/images';
import {debounce} from './../core/utils';
import {handleErrorResponse} from '../core/messages/store';
import {urlParams as UrlParams} from '../core/utils';
Expand Down Expand Up @@ -74,6 +75,7 @@ export default {
openTab: null,
userUpdatedVolareResolution: false,
userId: null,
crossOriginError: false,
};
},
computed: {
Expand Down Expand Up @@ -133,6 +135,9 @@ export default {
return imagesIds;
},
hasCrossOriginError() {
return !this.loading && this.crossOriginError;
},
},
methods: {
getImageAndAnnotationsPromises(id) {
Expand Down Expand Up @@ -524,7 +529,11 @@ export default {
Settings.delete('openTab');
},
handleLoadingError(message) {
Messages.danger(message);
if (message instanceof CrossOriginError) {
this.crossOriginError = true;
} else {
Messages.danger(message);
}
},
createSampledAnnotation() {
this.$refs.canvas.createSampledAnnotation();
Expand Down Expand Up @@ -553,6 +562,7 @@ export default {
imageId(id) {
if (id) {
this.startLoading();
this.crossOriginError = false;
Vue.Promise.all(this.getImageAndAnnotationsPromises(id))
.then(this.setCurrentImageAndAnnotations)
.then(this.updateUrlSlug)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@ let magicWandInteraction;
export default {
computed: {
crossOrigin() {
return this.image && this.image.crossOrigin;
},
isMagicWanding() {
return this.interactionMode === 'magicWand' && !this.crossOrigin;
return this.interactionMode === 'magicWand';
},
},
methods: {
Expand All @@ -38,7 +35,7 @@ export default {
maybeSetMagicWandLayer(image, oldImage) {
// Swap source layers for the magic wand interaction if image types
// change.
if (image && !this.crossOrigin) {
if (image) {
if (image.tiled === true) {
if (!oldImage || oldImage.tiled !== true) {
magicWandInteraction.setLayer(this.tiledImageLayer);
Expand Down
3 changes: 0 additions & 3 deletions resources/assets/js/annotations/components/settingsTab.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,6 @@ export default {
plugins() {
return plugins;
},
crossOrigin() {
return this.image && this.image.crossOrigin;
},
settings() {
return Settings;
},
Expand Down
14 changes: 6 additions & 8 deletions resources/assets/js/annotations/stores/images.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import Events from '../../core/events';
import fx from '../vendor/glfx';
export class CrossOriginError extends Error {}
/**
* Store for the images of the annotation tool
*/
Expand Down Expand Up @@ -103,7 +105,7 @@ export default new Vue({
});
},
checkSupportsColorAdjustment(image) {
if (!this.fxCanvas || image.crossOrigin) {
if (!this.fxCanvas) {
return false;
}
Expand Down Expand Up @@ -148,7 +150,6 @@ export default new Vue({
width: 0,
height: 0,
canvas: document.createElement('canvas'),
crossOrigin: false,
};
// Disable auto-rotation. Otherwise the canvas element might use the
Expand Down Expand Up @@ -198,13 +199,10 @@ export default new Vue({
.catch(function (response) {
// I could not find any reliable way to detect a failure due to
// blocking of CORS. But the status seemed to be always 0.
// If CORS is blocked, we can still display the image but have to
// disable a few features that require reading the image data.
// Cross-origin images without CORS are no longer supported.
// see: https://github.com/biigle/core/issues/351
if (response.status === 0) {
imageWrapper.crossOrigin = true;
img.src = response.url;
return promise;
throw new CrossOriginError();
}
return Vue.Promise.reject(`Failed to load image ${id}!`);
Expand Down
8 changes: 5 additions & 3 deletions resources/assets/js/core/components/loaderBlock.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<template>
<div class="loader-block" :class="classObject">
<message-curtain :class="classObject">
<loader :active="active" :fancy="true"></loader>
</div>
</message-curtain>
</template>

<script>
import Loader from './loader';
import MessageCurtain from './messageCurtain';
/**
* A component for a loading spinner element that acts as blocking overlay
Expand All @@ -15,6 +16,7 @@ import Loader from './loader';
export default {
components: {
loader: Loader,
messageCurtain: MessageCurtain,
},
props: {
active: {
Expand All @@ -25,7 +27,7 @@ export default {
computed: {
classObject() {
return {
'loader-block--active': this.active,
'loader-block--inactive': !this.active,
};
},
},
Expand Down
21 changes: 21 additions & 0 deletions resources/assets/js/core/components/messageCurtain.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<template>
<div class="message-curtain">
<slot><span class="lead" v-text="text"></span></slot>
</div>
</template>

<script>
/**
* A component for a "curtain" that covers/blocks the whole parent element.
*
* @type {Object}
*/
export default {
props: {
text: {
type: String,
default: '',
},
},
};
</script>
2 changes: 2 additions & 0 deletions resources/assets/js/core/mixins/loader.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script>
import LoaderComponent from '../components/loader';
import LoaderBlockComponent from '../components/loaderBlock';
import MessageCurtainComponent from '../components/messageCurtain';
import Messages from './../messages/store';
/**
Expand All @@ -12,6 +13,7 @@ export default {
components: {
loader: LoaderComponent,
loaderBlock: LoaderBlockComponent,
messageCurtain: MessageCurtainComponent,
},
data() {
return {
Expand Down
18 changes: 2 additions & 16 deletions resources/assets/sass/components/_loaderBlock.scss
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@
.loader-block {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.message-curtain.loader-block--inactive {
pointer-events: none;
transition: .25s background-color ease;
z-index: 999;

&.loader-block--active {
pointer-events: auto;
background-color: rgba(0, 0, 0, .5);
}
background-color: transparent;
}
14 changes: 14 additions & 0 deletions resources/assets/sass/components/_messageCurtain.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.message-curtain {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
pointer-events: auto;
transition: .25s background-color ease;
z-index: 999;
background-color: rgba(0, 0, 0, .5);
}
1 change: 1 addition & 0 deletions resources/assets/sass/main.scss
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
@import "components/sidebar";
@import "components/membersPanel";
@import "components/powerToggle";
@import "components/messageCurtain";
@import "label-trees/main";
@import "projects/main";
@import "volumes/main";
Expand Down
5 changes: 5 additions & 0 deletions resources/views/annotations/show.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@
<div id="annotator-container" class="sidebar-container" v-cloak>
<div class="sidebar-container__content">
<loader-block :active="loading"></loader-block>
<message-curtain v-if="hasCrossOriginError" v-cloak>
<span class="lead text-danger">
Please configure <a href="/manual/tutorials/volumes/remote-volumes#cors">cross origin resource sharing</a> on your remote image location.
</span>
</message-curtain>
<annotation-canvas
:can-add="isEditor"
:can-modify="isEditor"
Expand Down
3 changes: 1 addition & 2 deletions resources/views/annotations/show/annotationCanvas.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@
<control-button v-cloak icon="fa-paint-brush" title="Draw a polygon using the brush tool 𝗘" :active="isUsingPolygonBrush" v-on:click="togglePolygonBrush"></control-button>
<control-button v-cloak icon="fa-eraser" title="Modify selected polygons using the eraser tool 𝗥" :active="isUsingPolygonEraser" v-on:click="togglePolygonEraser"></control-button>
<control-button v-cloak icon="fa-fill-drip" title="Modify selected polygons using the fill tool 𝗧" :active="isUsingPolygonFill" v-on:click="togglePolygonFill"></control-button>
<control-button v-if="crossOrigin" icon="fa-magic" title="The magic wand tool is not available for remote images without cross-origin resource sharing" :disabled="true"></control-button>
<control-button v-else v-cloak icon="fa-magic" title="Draw a polygon using the magic wand tool 𝗦𝗵𝗶𝗳𝘁+𝗚" :active="isMagicWanding" v-on:click="toggleMagicWand"></control-button>
<control-button v-cloak icon="fa-magic" title="Draw a polygon using the magic wand tool 𝗦𝗵𝗶𝗳𝘁+𝗚" :active="isMagicWanding" v-on:click="toggleMagicWand"></control-button>
</control-button>
</div>
<div class="btn-group edit-controls">
Expand Down
3 changes: 1 addition & 2 deletions resources/views/annotations/show/tabs/settings.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
<settings-tab v-cloak :image="image" v-on:change="handleSettingsChange" inline-template>
<div class="annotator-tab">
<div class="sidebar-tab__section">
<button v-if="crossOrigin" class="btn btn-default" title="Screenshots are not available for remote images without cross-origin resource sharing" disabled="disabled" ><span class="fa fa-camera" aria-hidden="true"></span> Capture screenshot</button>
<screenshot-button v-else inline-template>
<screenshot-button inline-template>
<button class="btn btn-default" title="Get a screenshot of the visible area" v-on:click="capture"><span class="fa fa-camera" aria-hidden="true"></span> Capture screenshot</button>
</screenshot-button>
</div>
Expand Down
24 changes: 10 additions & 14 deletions resources/views/manual/tutorials/volumes/remote-volumes.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,30 +36,26 @@
</p>

<h3><a name="cors"></a>Cross-Origin Resource Sharing</h3>

<div class="panel panel-info">
<div class="panel-body text-info">
BIIGLE used to support remote images without CORS but this is no longer possible because of recent changes in web browsers.
</div>
</div>

<p>
The cross-origin policy is a security mechanism of web browsers that prevents malicious third parties from extracting sensitive information from your web pages. This includes cases like loading files from remote sources in BIIGLE. Although those files can be displayed, BIIGLE cannot access the raw data in the browser which is a requirement for some features of the image annotation tool. <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">Cross-Origin Resource Sharing</a> (CORS) is a mechanism to manually configure exceptions for the cross-origin policy. With a correct CORS configuration, BIIGLE can process files from remote sources just like regular files.
The cross-origin policy is a security mechanism of web browsers that prevents malicious third parties from extracting sensitive information from your web pages. This includes cases like loading files from remote sources in BIIGLE. <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS">Cross-Origin Resource Sharing</a> (CORS) is a mechanism to manually configure exceptions for the cross-origin policy. BIIGLE can only process files from remote sources that have a correct CORS configuration.
</p>
<p>
To set up CORS for the files of your remote source, you have to update the configuration of the webserver that serves the files. Some cloud storage services specifically provide configuration options for CORS. The webserver has to add the following HTTP headers to any <code>GET</code> or <code>OPTIONS</code> HTTP request for an image:
To set up CORS for the files of your remote source, you have to update the configuration of the webserver that serves the files. Some cloud storage services specifically provide configuration options for CORS. The webserver has to add the following HTTP headers to any <code>GET</code> or <code>OPTIONS</code> HTTP request for a file:
</p>
<pre>
Access-Control-Allow-Origin "*"
Access-Control-Allow-Headers "x-csrf-token, x-requested-with"
</pre>
<p>
In addition to that, you have to use a secure HTTP connection (<code>https://</code>) to access the files. BIIGLE detects if CORS is properly configured for remote files and automatically enables or disables the respective features. The features that are disabled for remote images without CORS are:
In addition to that, you have to use a secure HTTP connection (<code>https://</code>) to access the files.
</p>
<ul>
<li>
Live color adjustment of images in the image annotation tool.
</li>
<li>
The magic wand interaction in the image annotation tool.
</li>
<li>
The screenshot button in the image annotation tool. Use the usual functions of your operating system or browser to create screenshots manually.
</li>
</ul>

<h3><a name="how-to-secure"></a>How to secure a remote location</h3>
<div class="panel panel-warning">
Expand Down

0 comments on commit 7c9e380

Please sign in to comment.