diff --git a/main/handlers/api/handlers.go b/main/handlers/api/handlers.go
index ec4e82e5a..a6f3ea2f0 100644
--- a/main/handlers/api/handlers.go
+++ b/main/handlers/api/handlers.go
@@ -632,6 +632,14 @@ func LoginHandler(e echo.Context) (err error) {
})
}
+// Validates user session cookie
+func ValidateUserSession(e echo.Context) (err error) {
+ c := e.(*www.Context)
+ c.Session(true)
+
+ return c.NoContent(http.StatusOK)
+}
+
// Returns an object containing
//
// {
@@ -1133,7 +1141,7 @@ func GetProfilePhotoHandler(e echo.Context) error {
err := userService.GetProfilePhoto(sess, id, c.Response().Writer)
if err != nil {
- return c.NoContent(http.StatusNotFound)
+ return c.NoContent(http.StatusNoContent)
}
c.Response().Committed = true
c.Response().Header().Set("Content-Type", "image/jpeg")
diff --git a/main/handlers/routes.go b/main/handlers/routes.go
index a8d354d8c..ff00873dc 100644
--- a/main/handlers/routes.go
+++ b/main/handlers/routes.go
@@ -66,6 +66,7 @@ func MainHostedAPI(e *echo.Echo, s *www.Security, version string) {
{GET, PUBLIC, "/api/config", api.ConfigHandler(version)},
// authentication
+ {GET, PUBLIC, "/api/session/validate", api.ValidateUserSession},
{GET, PUBLIC, "/api/session/token", api.GetSessionTokenHandler},
{DELETE, USER, "/api/session/token", api.DeleteSessionTokenHandler},
{GET, PUBLIC, "/api/challenge", api.ChallengeHandler}, // Need session
diff --git a/ui/core/src/baseApp.js b/ui/core/src/baseApp.js
index 0d7e1b687..4db360309 100644
--- a/ui/core/src/baseApp.js
+++ b/ui/core/src/baseApp.js
@@ -73,15 +73,13 @@ export default {
if (this.me.role >= 100) {
return true
}
- } catch (e) {
- }
+ } catch (e) {}
try {
// check public
if (item.publicByID[0] === 2) {
return true
}
- } catch (e) {
- }
+ } catch (e) {}
try {
// check owner
@@ -92,8 +90,7 @@ export default {
item.grant[this.me.id][0] === 2) {
return true
}
- } catch (e) {
- }
+ } catch (e) {}
try {
// check group
if (this.me.role !== 0) {
@@ -102,8 +99,7 @@ export default {
return true
}
}
- } catch (e) {
- }
+ } catch (e) {}
// check others
try {
@@ -111,8 +107,7 @@ export default {
// others have write rights
return true
}
- } catch (e) {
- }
+ } catch (e) {}
return false
},
handleError (o) {
@@ -152,14 +147,29 @@ export default {
},
setSelectedLang (lang) {
if (lang) {
- this.$cookie.set('lang', lang, { expires: '1Y' })
+ this.$cookie.set('lang', lang, {
+ expires: '1Y'
+ })
this.reloadI18n()
} else {
this.$cookie.delete('lang')
this.$i18n.set(this.fallbackLang())
}
},
+ checkUserHasSession () {
+ return !!localStorage.getItem('userhassession')
+ },
+ initUserHasSession () {
+ localStorage.setItem('userhassession', true)
+ },
+ deleteUserHasSession () {
+ localStorage.removeItem('userhassession')
+ },
loadMe (clb) {
+ if (!this.checkUserHasSession()) {
+ return
+ }
+
axios.get('/api/me').then((response) => {
this.me = response.data
this.$root.$emit('me', this.me)
@@ -171,6 +181,7 @@ export default {
}
}
}, (err) => {
+ this.deleteUserHasSession()
this.handleError(err)
})
},
@@ -305,6 +316,9 @@ export default {
this.handleError(err)
})
},
+ async validateSessionCookie () {
+ return axios.get('/api/session/validate')
+ },
loadMeta (clb) {
axios.get('/api/i18n/meta').then((response) => {
this.meta = response.data
@@ -396,14 +410,14 @@ export default {
get () {
return this.$root.$children[0]
},
- set (a) {
- }
+ set (a) {}
}
},
created () {
const tmpLangToPreventFromWarnings = 'en'
this.$i18n.fallback(tmpLangToPreventFromWarnings)
this.$i18n.set(tmpLangToPreventFromWarnings)
+ this.validateSessionCookie()
this.loadMeta()
this.loadConfig()
this.loadMe()
diff --git a/ui/core/src/components/user/TopRightProfile.vue b/ui/core/src/components/user/TopRightProfile.vue
index 6a62dc1d9..7a9da6224 100644
--- a/ui/core/src/components/user/TopRightProfile.vue
+++ b/ui/core/src/components/user/TopRightProfile.vue
@@ -172,6 +172,7 @@ export default {
},
logout () {
axios.post('/api/logout', null).then(response => {
+ this.app.deleteUserHasSession()
window.location.replace('/')
}, (err) => {
this.app.handleError(err)
diff --git a/ui/core/src/libs/legacy/global.js b/ui/core/src/libs/legacy/global.js
index 669707bd1..3a2370511 100644
--- a/ui/core/src/libs/legacy/global.js
+++ b/ui/core/src/libs/legacy/global.js
@@ -86,7 +86,8 @@ var cglbl = {
return this.scrollBarWith
},
sizeOf: function (obj) {
- var size = 0; var key
+ var size = 0;
+ var key
for (key in obj) {
if (obj.hasOwnProperty(key)) size++
}
@@ -203,7 +204,9 @@ $.fn.checkAndFillInputValue = function (fullKey, dataVal, element) {
_.parents('.field-parent').attr('data-status', 'success')
_.attr('data-status', 'success')
_.doCompAction(true)
- _.trigger('change', [{ init: true }])
+ _.trigger('change', [{
+ init: true
+ }])
return true
}
} else if (_.attr('type') === 'checkbox') {
@@ -214,7 +217,9 @@ $.fn.checkAndFillInputValue = function (fullKey, dataVal, element) {
_.parents('.field-parent').attr('data-status', 'success')
_.attr('data-status', 'success')
_.doCompAction(true)
- _.trigger('change', [{ init: true }])
+ _.trigger('change', [{
+ init: true
+ }])
return true
} else { // multi
_.removeAttr('checked')
@@ -223,7 +228,9 @@ $.fn.checkAndFillInputValue = function (fullKey, dataVal, element) {
_.parents('.field-parent').attr('data-status', 'success')
_.attr('data-status', 'success')
_.doCompAction(true)
- _.trigger('change', [{ init: true }])
+ _.trigger('change', [{
+ init: true
+ }])
return true
}
}
@@ -268,7 +275,9 @@ $.fn.checkAndFillInputValue = function (fullKey, dataVal, element) {
_.attr('data-status', 'success')
_.doCompAction(true)
if (_.is('select')) {
- _.trigger('change', [{ init: true }])
+ _.trigger('change', [{
+ init: true
+ }])
}
return true
} else {
@@ -353,7 +362,9 @@ FTGlobal.prototype.createDatepicker = function (elem, options) {
pickr.setDate(elem.val())
}
}
- } catch (e) { console.log(e) }
+ } catch (e) {
+ console.log(e)
+ }
} catch (dateExc) {
console.log(dateExc)
}
@@ -438,7 +449,7 @@ $.fn.nextParentWithClass = function (attrClass) {
var parent = this
while (!parent.hasClass(attrClass)) {
parent = parent.parent()
- ++index
+ ++index
if (maxIndex < index) {
break
}
@@ -608,9 +619,9 @@ $.fn.fileElement = function (element) {
}
jQuery.fn.center = function () {
this.css('margin-top', Math.max(0, (($(window).height() - $(this).outerHeight()) / 2) +
- $(window).scrollTop()) + 'px')
+ $(window).scrollTop()) + 'px')
this.css('margin-left', Math.max(0, (($(window).width() - $(this).outerWidth()) / 2) +
- $(window).scrollLeft()) + 'px')
+ $(window).scrollLeft()) + 'px')
return this
}
@@ -726,7 +737,9 @@ $.fn.assignSubmitOnChange = function (options) {
if ($.isFunction(options.success)) {
options.success.apply(this, [data, textStatus, xhr, this.myReq])
}
- } catch (eee) { console.log(eee) }
+ } catch (eee) {
+ console.log(eee)
+ }
},
error: function (data, a2, a3, a4) {
var status = 'error'
@@ -738,13 +751,20 @@ $.fn.assignSubmitOnChange = function (options) {
this.responsibleEl.showFieldErrors(data.responseJSON)
}
if (data.status == 502) {
- this.responsibleEl.showFieldErrors({ errors: [{ field: this.responsibleEl.attr('name'), message: FTG.translate('file.limit.exceeded') }] })
+ this.responsibleEl.showFieldErrors({
+ errors: [{
+ field: this.responsibleEl.attr('name'),
+ message: FTG.translate('file.limit.exceeded')
+ }]
+ })
}
try {
if ($.isFunction(options.error)) {
options.error.apply(this, [data, a2, a3, this.myReq])
}
- } catch (eee) { console.log(eee) }
+ } catch (eee) {
+ console.log(eee)
+ }
}
})
}
@@ -950,7 +970,7 @@ $.fn.serializeFormToObject = function (excludeFileFormFields) {
for (var i = 0; i < allElementsHavingNameAttr.length; ++i) {
targetElement = $(allElementsHavingNameAttr[i])
// if (excludeFileFormFields === true) {
- if (!targetElement.is(':visible') || targetElement.attr('type') === 'file') {
+ if (targetElement.attr('type') === 'file') {
continue
}
// }
@@ -1008,7 +1028,9 @@ $.fn.isValAsFilenameValid = function () {
$.fn.makeSameHeight = function (options) {
var _this = this
if (!options) {
- options = { delay: 10 }
+ options = {
+ delay: 10
+ }
}
if (!options.delay) {
options.delay = 10
@@ -1045,35 +1067,103 @@ String.prototype.msgFormat = function () {
})
}
jQuery.fn.copyHtmlToClipboard = function () {
- if (this.length === 0) {
- return this
- }
- var node = this[0]
- try {
- var range, selection
- selection = window.getSelection()
- range = document.createRange()
- range.selectNodeContents(node)
- selection.removeAllRanges()
- selection.addRange(range)
- document.execCommand('copy')
- $('#copyToclipboardInfo_').remove()
- var cInfo = $('copied to clipboard')
- $(node).append(cInfo)
- setTimeout(function () {
- cInfo.remove()
- }, 4000)
- } catch (e) {
- console.log(e)
+ if (this.length === 0) {
+ return this
+ }
+ var node = this[0]
+ try {
+ var range, selection
+ selection = window.getSelection()
+ range = document.createRange()
+ range.selectNodeContents(node)
+ selection.removeAllRanges()
+ selection.addRange(range)
+ document.execCommand('copy')
+ $('#copyToclipboardInfo_').remove()
+ var cInfo = $('copied to clipboard')
+ $(node).append(cInfo)
+ setTimeout(function () {
+ cInfo.remove()
+ }, 4000)
+ } catch (e) {
+ console.log(e)
+ }
}
-}
-/*!
- * JavaScript Cookie v2.1.2
- * https://github.com/js-cookie/js-cookie
- *
- * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
- * Released under the MIT license
- */
-!(function (e) { if (typeof define === 'function' && define.amd)define(e); else if (typeof exports === 'object')module.exports = e(); else { var n = window.Cookies; var t = window.Cookies = e(); t.noConflict = function () { return window.Cookies = n, t } } }(function () { function e () { for (var e = 0, n = {}; e < arguments.length; e++) { var t = arguments[e]; for (var o in t)n[o] = t[o] } return n } function n (t) { function o (n, r, i) { var c; if (typeof document !== 'undefined') { if (arguments.length > 1) { if (i = e({ path: '/' }, o.defaults, i), typeof i.expires === 'number') { var a = new Date(); a.setMilliseconds(a.getMilliseconds() + 864e5 * i.expires), i.expires = a } try { c = JSON.stringify(r), /^[\{\[]/.test(c) && (r = c) } catch (s) {} return r = t.write ? t.write(r, n) : encodeURIComponent(String(r)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent), n = encodeURIComponent(String(n)), n = n.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent), n = n.replace(/[\(\)]/g, escape), document.cookie = [n, '=', r, i.expires ? '; expires=' + i.expires.toUTCString() : '', i.path ? '; path=' + i.path : '', i.domain ? '; domain=' + i.domain : '', i.secure ? '; secure' : ''].join('') }n || (c = {}); for (var p = document.cookie ? document.cookie.split('; ') : [], u = /(%[0-9A-Z]{2})+/g, d = 0; d < p.length; d++) { var f = p[d].split('='); var l = f.slice(1).join('='); l.charAt(0) === '"' && (l = l.slice(1, -1)); try { var m = f[0].replace(u, decodeURIComponent); if (l = t.read ? t.read(l, m) : t(l, m) || l.replace(u, decodeURIComponent), this.json) try { l = JSON.parse(l) } catch (s) {} if (n === m) { c = l; break }n || (c[m] = l) } catch (s) {} } return c } } return o.set = o, o.get = function (e) { return o(e) }, o.getJSON = function () { return o.apply({ json: !0 }, [].slice.call(arguments)) }, o.defaults = {}, o.remove = function (n, t) { o(n, '', e(t, { expires: -1 })) }, o.withConverter = n, o } return n(function () {}) }))
+ /*!
+ * JavaScript Cookie v2.1.2
+ * https://github.com/js-cookie/js-cookie
+ *
+ * Copyright 2006, 2015 Klaus Hartl & Fagner Brack
+ * Released under the MIT license
+ */
+ !(function (e) {
+ if (typeof define === 'function' && define.amd) define(e);
+ else if (typeof exports === 'object') module.exports = e();
+ else {
+ var n = window.Cookies;
+ var t = window.Cookies = e();
+ t.noConflict = function () {
+ return window.Cookies = n, t
+ }
+ }
+ }(function () {
+ function e() {
+ for (var e = 0, n = {}; e < arguments.length; e++) {
+ var t = arguments[e];
+ for (var o in t) n[o] = t[o]
+ }
+ return n
+ }
+
+ function n(t) {
+ function o(n, r, i) {
+ var c;
+ if (typeof document !== 'undefined') {
+ if (arguments.length > 1) {
+ if (i = e({
+ path: '/'
+ }, o.defaults, i), typeof i.expires === 'number') {
+ var a = new Date();
+ a.setMilliseconds(a.getMilliseconds() + 864e5 * i.expires), i.expires = a
+ }
+ try {
+ c = JSON.stringify(r), /^[\{\[]/.test(c) && (r = c)
+ } catch (s) {}
+ return r = t.write ? t.write(r, n) : encodeURIComponent(String(r)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent), n = encodeURIComponent(String(n)), n = n.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent), n = n.replace(/[\(\)]/g, escape), document.cookie = [n, '=', r, i.expires ? '; expires=' + i.expires.toUTCString() : '', i.path ? '; path=' + i.path : '', i.domain ? '; domain=' + i.domain : '', i.secure ? '; secure' : ''].join('')
+ }
+ n || (c = {});
+ for (var p = document.cookie ? document.cookie.split('; ') : [], u = /(%[0-9A-Z]{2})+/g, d = 0; d < p.length; d++) {
+ var f = p[d].split('=');
+ var l = f.slice(1).join('=');
+ l.charAt(0) === '"' && (l = l.slice(1, -1));
+ try {
+ var m = f[0].replace(u, decodeURIComponent);
+ if (l = t.read ? t.read(l, m) : t(l, m) || l.replace(u, decodeURIComponent), this.json) try {
+ l = JSON.parse(l)
+ } catch (s) {}
+ if (n === m) {
+ c = l;
+ break
+ }
+ n || (c[m] = l)
+ } catch (s) {}
+ }
+ return c
+ }
+ }
+ return o.set = o, o.get = function (e) {
+ return o(e)
+ }, o.getJSON = function () {
+ return o.apply({
+ json: !0
+ }, [].slice.call(arguments))
+ }, o.defaults = {}, o.remove = function (n, t) {
+ o(n, '', e(t, {
+ expires: -1
+ }))
+ }, o.withConverter = n, o
+ }
+ return n(function () {})
+ }))
export default FTG
diff --git a/ui/core/src/views/AdminLogin.vue b/ui/core/src/views/AdminLogin.vue
index e0545232b..e9eedafd4 100644
--- a/ui/core/src/views/AdminLogin.vue
+++ b/ui/core/src/views/AdminLogin.vue
@@ -94,9 +94,11 @@ export default {
this.pwlogin = true
if (this.checkTermsAndConditions()) {
axios.post('/api/login', { email: this.email, password: this.password }).then(res => {
+ this.app.initUserHasSession()
this.hasError = false
window.location = res.data.location || '/admin/workflow'
}, (err) => {
+ this.app.deleteUserHasSession()
this.app.handleError(err)
this.loginErrorMessage = this.$t('You have entered an invalid username or password')
this.hasError = true
@@ -135,6 +137,7 @@ export default {
this.challenge = response.data
this.metamaskLogin()
}, (err) => {
+ this.app.deleteUserHasSession()
this.app.handleError(err)
})
}
@@ -149,36 +152,50 @@ export default {
await this.app.wallet.wallet.setupDefaultAccount()
} catch (e) {
console.log(e)
+ this.app.deleteUserHasSession()
this.walletErrorMessage = this.$t('Please grant access to MetaMask.')
return
}
} else {
+ this.app.deleteUserHasSession()
this.walletErrorMessage = this.$t('Please grant access to MetaMask.')
return
}
+
this.account = this.app.wallet.getCurrentAddress()
+
if (this.account === undefined) {
+ this.app.deleteUserHasSession()
this.walletErrorMessage = this.$t('Please sign in to MetaMask.')
return
}
+
this.pwlogin = false
+
if (this.checkTermsAndConditions()) {
- this.app.wallet.signMessage(this.challenge, this.account).then((signature) => {
- axios.post('/api/login', { signature }).then((res) => {
- this.challenge = ''
- if (res.status >= 200 && res.status <= 299) {
- window.location = res.data.location || '/admin/workflow'
- } else {
- this.walletErrorMessage = this.$t('Could not verify signature.')
- }
- }, (err) => {
- this.challenge = ''
- this.app.handleError(err)
- this.walletErrorMessage = this.$t('Could not verify signature.')
+ this.app.wallet.signMessage(this.challenge, this.account)
+ .then((signature) => {
+ axios.post('/api/login', { signature })
+ .then((res) => {
+ this.challenge = ''
+
+ if (res.status >= 200 && res.status <= 299) {
+ this.app.initUserHasSession()
+ window.location = res.data.location || '/admin/workflow'
+ } else {
+ this.app.deleteUserHasSession()
+ this.walletErrorMessage = this.$t('Could not verify signature.')
+ }
+ }, (err) => {
+ this.challenge = ''
+ this.app.deleteUserHasSession()
+ this.app.handleError(err)
+ this.walletErrorMessage = this.$t('Could not verify signature.')
+ })
+ }).catch(() => {
+ this.app.deleteUserHasSession()
+ this.walletErrorMessage = this.$t('Could not Sign Message.')
})
- }).catch(() => {
- this.walletErrorMessage = this.$t('Could not Sign Message.')
- })
}
}
}