Rename all cookies to be instance-independent (Requires relogin) - fixes #94
Change-Id: Icd8b58e4b6fbd99a93ee6972485ef77786b5764c
diff --git a/Changes b/Changes
index bce0872..656df7a 100755
--- a/Changes
+++ b/Changes
@@ -1,3 +1,11 @@
+0.36 2019-07-23
+ - Rename all cookies to be independent
+ for different instance (#94).
+ - Enable https only via
+ configuration option 'https_only'.
+
+ WARNING: This requires relogin for all users!
+
0.35 2019-07-19
- Added EXPERIMENTAL proxy to API route.
- Remove deprecated api configuration
diff --git a/dev/js/runner/all.html b/dev/js/runner/all.html
index 366c755..f0a71ad 100644
--- a/dev/js/runner/all.html
+++ b/dev/js/runner/all.html
@@ -47,7 +47,8 @@
'spec/corpusByMatchSpec',
'spec/statSpec',
'spec/vcSpec',
- 'spec/tourSpec'
+ 'spec/tourSpec',
+ 'spec/utilSpec'
],
function () {
window.onload();
diff --git a/dev/js/spec/utilSpec.js b/dev/js/spec/utilSpec.js
new file mode 100644
index 0000000..b42102a
--- /dev/null
+++ b/dev/js/spec/utilSpec.js
@@ -0,0 +1,19 @@
+define(['util'], function () {
+ describe('KorAP.util', function () {
+
+ it('should quote', function () {
+ expect('Baum'.quote()).toEqual('"Baum"');
+ expect('B"a"um'.quote()).toEqual('"B\\"a\\"um"');
+ });
+
+ it('should escape regex', function () {
+ expect('aaa/bbb\/ccc'.escapeRegex()).toEqual('aaa\\/bbb\\/ccc');
+ });
+
+ it('should slugify', function () {
+ expect('/korap/test'.slugify()).toEqual('koraptest');
+ expect('korap test'.slugify()).toEqual('korap-test');
+ expect('Korap Test'.slugify()).toEqual('korap-test');
+ });
+ })
+});
diff --git a/dev/js/src/init.js b/dev/js/src/init.js
index 6de66d8..bafa473 100644
--- a/dev/js/src/init.js
+++ b/dev/js/src/init.js
@@ -44,7 +44,10 @@
const d = document;
- KorAP.session = sessionClass.create('KalamarJS');
+ // Create suffix if KorAP is run in a subfolder
+ KorAP.session = sessionClass.create(
+ KorAP.URL.length > 0 ? 'kalamarJS-' + KorAP.URL.slugify() : 'kalamarJS'
+ );
// Override KorAP.log
window.alertify = alertifyClass;
diff --git a/dev/js/src/util.js b/dev/js/src/util.js
index cb60c2a..5df346b 100644
--- a/dev/js/src/util.js
+++ b/dev/js/src/util.js
@@ -9,16 +9,22 @@
};
};
-var _quoteRE = new RegExp("([\"\\\\])", 'g');
+const _quoteRE = new RegExp("([\"\\\\])", 'g');
String.prototype.quote = function () {
- return this.replace(_quoteRE, '\\$1');
+ return '"' + this.replace(_quoteRE, '\\$1') + '"';
};
-var _escapeRE = new RegExp("([\/\\\\])", 'g');
+const _escapeRE = new RegExp("([\/\\\\])", 'g');
String.prototype.escapeRegex = function () {
return this.replace(_escapeRE, '\\$1');
};
+const _slug1RE = new RegExp("[^-a-zA-Z0-9_\\s]+", 'g');
+const _slug2RE = new RegExp("[-\\s]+", 'g');
+String.prototype.slugify = function () {
+ return this.toLowerCase().replace(_slug1RE, '').replace(_slug2RE, '-');
+};
+
// Add toggleClass method similar to jquery
HTMLElement.prototype.toggleClass = function (c1, c2) {
var cl = this.classList;
diff --git a/dev/js/src/vc/doc.js b/dev/js/src/vc/doc.js
index b012e1f..8ea8364 100644
--- a/dev/js/src/vc/doc.js
+++ b/dev/js/src/vc/doc.js
@@ -667,7 +667,7 @@
return string + '/' + this.value().escapeRegex() + '/';
case "string":
case "text":
- return string + '"' + this.value().quote() + '"';
+ return string + this.value().quote();
};
return "";
diff --git a/dev/js/src/vc/docgroupref.js b/dev/js/src/vc/docgroupref.js
index 9425fb3..e72385a 100644
--- a/dev/js/src/vc/docgroupref.js
+++ b/dev/js/src/vc/docgroupref.js
@@ -273,7 +273,7 @@
return "";
// Build doc string based on key
- return 'referTo "' + this.ref().quote() + '"';
+ return 'referTo ' + this.ref().quote();
}
};
});
diff --git a/dev/js/src/vc/fragment.js b/dev/js/src/vc/fragment.js
index 2d2504d..f0c1397 100644
--- a/dev/js/src/vc/fragment.js
+++ b/dev/js/src/vc/fragment.js
@@ -176,7 +176,7 @@
if (item[2] === "date") {
return item[0] + ' in ' + item[1];
};
- return item[0] + ' = "' + new String(item[1]).quote() + '"';
+ return item[0] + ' = ' + new String(item[1]).quote();
}
).join(" & ");
}
diff --git a/dev/js/src/vc/rewrite.js b/dev/js/src/vc/rewrite.js
index eeec713..150ee87 100644
--- a/dev/js/src/vc/rewrite.js
+++ b/dev/js/src/vc/rewrite.js
@@ -87,14 +87,9 @@
str += ' of ' + (
this._scope === null ?
'object' :
- '"' +
- this.scope().quote() +
- '"'
+ this.scope().quote()
);
- str += ' by ' +
- '"' +
- this.src().quote() +
- '"';
+ str += ' by ' + this.src().quote();
return str;
}
};
diff --git a/kalamar.conf b/kalamar.conf
index 6e61b15..a9d78b9 100644
--- a/kalamar.conf
+++ b/kalamar.conf
@@ -39,6 +39,9 @@
## Backend API version
# api_version => '1.0',
+ ## Run the application in a subfolder behind a proxy:
+ # proxy_prefix => '/korap',
+
## The name of the base corpus,
## for query examples (see kalamar.queries.dict)
# examplecorpus => 'dereko',
@@ -48,6 +51,9 @@
# plugins => [],
## Currently bundled: Piwik, Auth
+ ## Require everything to be send via https only:
+ # https_only => 1,
+
## Add experimental features:
# experimental_proxy => 1,
},
diff --git a/lib/Kalamar.pm b/lib/Kalamar.pm
index 3f82113..a72ead5 100644
--- a/lib/Kalamar.pm
+++ b/lib/Kalamar.pm
@@ -4,11 +4,11 @@
use Mojo::URL;
use Mojo::File;
use Mojo::JSON 'decode_json';
-use Mojo::Util qw/url_escape deprecated/;
+use Mojo::Util qw/url_escape deprecated slugify/;
use List::Util 'none';
# Minor version - may be patched from package.json
-our $VERSION = '0.35';
+our $VERSION = '0.36';
# Supported version of Backend API
our $API_VERSION = '1.0';
@@ -18,7 +18,6 @@
# TODO: Embed collection statistics
# TODO: Implement tab opener for matches and the tutorial
# TODO: Implement a "projects" system
-# TODO: Make authentification a plugin
# Start the application and register all routes and plugins
sub startup {
@@ -80,12 +79,21 @@
$self->log->warn('Kalamar-api_path not defined in configuration');
};
+ $self->sessions->cookie_name('kalamar');
+
+ # Require HTTPS
+ if ($conf->{https_only}) {
+
+ # ... for cookie transport
+ $self->sessions->secure(1);
+ };
+
+ # Run the app from a subdirectory
if ($conf->{proxy_prefix}) {
for ($self->sessions) {
$_->cookie_path($conf->{proxy_prefix});
- $_->cookie_name('kalamar');
- $_->secure(1);
+ $_->cookie_name('kalamar-' . slugify($conf->{proxy_prefix}));
};
# Set prefix in stash
diff --git a/package.json b/package.json
index 67cd9ea..9e58c1d 100755
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "Kalamar",
"description": "Mojolicious-based Frontend for KorAP",
"license": "BSD-2-Clause",
- "version": "0.35.2",
+ "version": "0.36.1",
"pluginVersion": "0.1",
"repository": {
"type": "git",
diff --git a/t/subfolder.t b/t/subfolder.t
new file mode 100644
index 0000000..01cf33e
--- /dev/null
+++ b/t/subfolder.t
@@ -0,0 +1,83 @@
+use Mojo::Base -strict;
+use Test::More;
+use Test::Mojo;
+use Mojo::File qw/path/;
+use utf8;
+
+my $t = Test::Mojo->new('Kalamar' => {
+ Kalamar => {
+ plugins => ['Auth']
+ }
+});
+
+$t->app->mode('production');
+
+$t->post_ok('/user/login' => form => { handle_or_email => 'test', pwd => 'fail' })
+ ->status_is(302)
+ ->header_is('Location' => '/');
+
+$t->get_ok('/')
+ ->status_is(200)
+ ->element_exists('link[rel=stylesheet][href^=/css/kalamar-]')
+ ->element_exists('script[src^=/js/kalamar-]')
+ ->element_exists('div.notify-error')
+ ->text_is('div.notify-error', 'Bad CSRF token')
+ ->element_exists('input[name=handle_or_email][value=test]')
+ ->element_exists_not('div.button.top a')
+ ->content_like(qr!KorAP\.URL = ''!)
+ ;
+
+is('kalamar',$t->app->sessions->cookie_name);
+ok(!$t->app->sessions->secure);
+
+$t = Test::Mojo->new('Kalamar' => {
+ Kalamar => {
+ plugins => ['Auth'],
+ https_only => 1
+ }
+});
+
+$t->post_ok('/user/login' => form => { handle_or_email => 'test', pwd => 'fail' })
+ ->status_is(302)
+ ->header_is('Location' => '/');
+
+$t->get_ok('/')
+ ->status_is(200)
+ ->element_exists_not('div.notify-error')
+ ;
+
+is('kalamar',$t->app->sessions->cookie_name);
+ok($t->app->sessions->secure);
+
+$t = Test::Mojo->new('Kalamar' => {
+ Kalamar => {
+ plugins => ['Auth'],
+ proxy_prefix => '/korap/test',
+ https_only => 1
+ }
+});
+
+$t->app->mode('production');
+
+$t->get_ok('/')
+ ->status_is(200)
+ ->element_exists('link[rel=stylesheet][href^=/korap/test/css/kalamar-]')
+ ->element_exists('script[src^=/korap/test/js/kalamar-]')
+ ;
+
+is('kalamar-koraptest',$t->app->sessions->cookie_name);
+ok($t->app->sessions->secure);
+
+$t->post_ok('/user/login' => form => { handle_or_email => 'test', pwd => 'fail' })
+ ->status_is(302)
+ ->header_is('Location' => '/');
+
+# Session can't be used
+$t->get_ok('/')
+ ->status_is(200)
+ ->element_exists_not('div.notify-error')
+ ->content_like(qr!KorAP\.URL = '/korap/test'!)
+ ;
+
+
+done_testing();