mirror of
https://github.com/simon987/sist2.git
synced 2025-04-10 14:06:45 +00:00
Add support for auth0
This commit is contained in:
parent
b9f008603a
commit
86ca9f1ecb
@ -1,7 +1,7 @@
|
|||||||
cmake_minimum_required(VERSION 3.7)
|
cmake_minimum_required(VERSION 3.7)
|
||||||
set(CMAKE_C_STANDARD 11)
|
|
||||||
|
|
||||||
project(sist2 C)
|
project(sist2)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
|
||||||
option(SIST_DEBUG "Build a debug executable" on)
|
option(SIST_DEBUG "Build a debug executable" on)
|
||||||
option(SIST_FAST "Enable more optimisation flags" off)
|
option(SIST_FAST "Enable more optimisation flags" off)
|
||||||
@ -40,9 +40,12 @@ add_executable(sist2
|
|||||||
src/stats.c src/stats.h src/ctx.c
|
src/stats.c src/stats.h src/ctx.c
|
||||||
src/parsing/sidecar.c src/parsing/sidecar.h
|
src/parsing/sidecar.c src/parsing/sidecar.h
|
||||||
|
|
||||||
|
src/auth0/auth0_c_api.h src/auth0/auth0_c_api.cpp
|
||||||
|
|
||||||
# argparse
|
# argparse
|
||||||
third-party/argparse/argparse.h third-party/argparse/argparse.c
|
third-party/argparse/argparse.h third-party/argparse/argparse.c
|
||||||
)
|
)
|
||||||
|
set_target_properties(sist2 PROPERTIES LINKER_LANGUAGE C)
|
||||||
|
|
||||||
target_link_directories(sist2 PRIVATE BEFORE ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/)
|
target_link_directories(sist2 PRIVATE BEFORE ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/)
|
||||||
set(CMAKE_FIND_LIBRARY_SUFFIXES .a .lib)
|
set(CMAKE_FIND_LIBRARY_SUFFIXES .a .lib)
|
||||||
|
@ -74,6 +74,10 @@ Web options
|
|||||||
--es-index=<str> Elasticsearch index name. DEFAULT=sist2
|
--es-index=<str> Elasticsearch index name. DEFAULT=sist2
|
||||||
--bind=<str> Listen on this address. DEFAULT=localhost:4090
|
--bind=<str> Listen on this address. DEFAULT=localhost:4090
|
||||||
--auth=<str> Basic auth in user:password format
|
--auth=<str> Basic auth in user:password format
|
||||||
|
--auth0-audience=<str> API audience/identifier
|
||||||
|
--auth0-domain=<str> Application domain
|
||||||
|
--auth0-client-id=<str> Application client ID
|
||||||
|
--auth0-public-key-file=<str> Path to Auth0 public key file extracted from <domain>/pem
|
||||||
--tag-auth=<str> Basic auth in user:password format for tagging
|
--tag-auth=<str> Basic auth in user:password format for tagging
|
||||||
--tagline=<str> Tagline in navbar
|
--tagline=<str> Tagline in navbar
|
||||||
--dev Serve html & js files from disk (for development)
|
--dev Serve html & js files from disk (for development)
|
||||||
@ -268,6 +272,7 @@ sist2 index --print ./my_index/ | jq | less
|
|||||||
* `--dev` Serve html & js files from disk (for development, used to modify frontend files without having to recompile)
|
* `--dev` Serve html & js files from disk (for development, used to modify frontend files without having to recompile)
|
||||||
* `--lang=<str>` Set the default web UI language (See #180 for a list of supported languages, default
|
* `--lang=<str>` Set the default web UI language (See #180 for a list of supported languages, default
|
||||||
is `en`). The user can change the language in the configuration page
|
is `en`). The user can change the language in the configuration page
|
||||||
|
* `--auth0-audience`, `--auth0-domain`, `--auth0-client-id`, `--auth0-public-key-file` See [Authentication with Auth0](auth0.md)
|
||||||
|
|
||||||
### Web examples
|
### Web examples
|
||||||
|
|
||||||
|
19
docs/auth0.md
Normal file
19
docs/auth0.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
# Authentication with Auth0
|
||||||
|
|
||||||
|
1. Create a new Auth0 application (Single page app)
|
||||||
|
2. Create a new Auth0 API:
|
||||||
|
1. Choose `RS256` signing algorithm
|
||||||
|
2. Set identifier (audience) to `https://sist2`
|
||||||
|
3. Download the Auth0 certificate from https://<domain>.auth0.com/pem (you can find the domain Applications->Basic information)
|
||||||
|
4. Extract the public key from the certificate using `openssl x509 -pubkey -noout -in cert.pem > pubkey.txt`
|
||||||
|
5. Start the sist2 web server
|
||||||
|
|
||||||
|
Example options:
|
||||||
|
```bash
|
||||||
|
sist2 web \
|
||||||
|
--auth0-client-id XXX \
|
||||||
|
--auth0-audience https://sist2 \
|
||||||
|
--auth0-domain YYY.auth0.com \
|
||||||
|
--auth0-public-key-file /ZZZ/pubkey.txt
|
||||||
|
```
|
2
sist2-admin/frontend/dist/index.html
vendored
2
sist2-admin/frontend/dist/index.html
vendored
@ -1 +1 @@
|
|||||||
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>sist2-admin</title><link href="css/app.0f0b676b.css" rel="preload" as="style"><link href="css/chunk-vendors.aa66c7e8.css" rel="preload" as="style"><link href="js/app.b34f501e.js" rel="preload" as="script"><link href="js/chunk-vendors.fad0ee6a.js" rel="preload" as="script"><link href="css/chunk-vendors.aa66c7e8.css" rel="stylesheet"><link href="css/app.0f0b676b.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but sist2-admin-vue doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.fad0ee6a.js"></script><script src="js/app.b34f501e.js"></script></body></html>
|
<!DOCTYPE html><html lang=""><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="icon" href="favicon.ico"><title>sist2-admin</title><link href="css/app.css" rel="preload" as="style"><link href="css/chunk-vendors.css" rel="preload" as="style"><link href="js/app.js" rel="preload" as="script"><link href="js/chunk-vendors.js" rel="preload" as="script"><link href="css/chunk-vendors.css" rel="stylesheet"><link href="css/app.css" rel="stylesheet"></head><body><noscript><strong>We're sorry but sist2-admin-vue doesn't work properly without JavaScript enabled. Please enable it to continue.</strong></noscript><div id="app"></div><script src="js/chunk-vendors.js"></script><script src="js/app.js"></script></body></html>
|
File diff suppressed because one or more lines are too long
@ -341,5 +341,4 @@ var e="jan._feb._mrt._apr._mei_jun._jul._aug._sep._okt._nov._dec.".split("_"),a=
|
|||||||
//! moment.js locale configuration
|
//! moment.js locale configuration
|
||||||
var e=t.defineLocale("x-pseudo",{months:"J~áñúá~rý_F~ébrú~árý_~Márc~h_Áp~ríl_~Máý_~Júñé~_Júl~ý_Áú~gúst~_Sép~témb~ér_Ó~ctób~ér_Ñ~óvém~bér_~Décé~mbér".split("_"),monthsShort:"J~áñ_~Féb_~Már_~Ápr_~Máý_~Júñ_~Júl_~Áúg_~Sép_~Óct_~Ñóv_~Déc".split("_"),monthsParseExact:!0,weekdays:"S~úñdá~ý_Mó~ñdáý~_Túé~sdáý~_Wéd~ñésd~áý_T~húrs~dáý_~Fríd~áý_S~átúr~dáý".split("_"),weekdaysShort:"S~úñ_~Móñ_~Túé_~Wéd_~Thú_~Frí_~Sát".split("_"),weekdaysMin:"S~ú_Mó~_Tú_~Wé_T~h_Fr~_Sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[T~ódá~ý át] LT",nextDay:"[T~ómó~rró~w át] LT",nextWeek:"dddd [át] LT",lastDay:"[Ý~ést~érdá~ý át] LT",lastWeek:"[L~ást] dddd [át] LT",sameElse:"L"},relativeTime:{future:"í~ñ %s",past:"%s á~gó",s:"á ~féw ~sécó~ñds",ss:"%d s~écóñ~ds",m:"á ~míñ~úté",mm:"%d m~íñú~tés",h:"á~ñ hó~úr",hh:"%d h~óúrs",d:"á ~dáý",dd:"%d d~áýs",M:"á ~móñ~th",MM:"%d m~óñt~hs",y:"á ~ýéár",yy:"%d ý~éárs"},dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10,a=1===~~(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+a},week:{dow:1,doy:4}});return e}))},fdbf:function(t,e,a){var n=a("4930");t.exports=n&&!Symbol.sham&&"symbol"==typeof Symbol.iterator},ffff:function(t,e,a){(function(t,e){e(a("c1df"))})(0,(function(t){"use strict";
|
var e=t.defineLocale("x-pseudo",{months:"J~áñúá~rý_F~ébrú~árý_~Márc~h_Áp~ríl_~Máý_~Júñé~_Júl~ý_Áú~gúst~_Sép~témb~ér_Ó~ctób~ér_Ñ~óvém~bér_~Décé~mbér".split("_"),monthsShort:"J~áñ_~Féb_~Már_~Ápr_~Máý_~Júñ_~Júl_~Áúg_~Sép_~Óct_~Ñóv_~Déc".split("_"),monthsParseExact:!0,weekdays:"S~úñdá~ý_Mó~ñdáý~_Túé~sdáý~_Wéd~ñésd~áý_T~húrs~dáý_~Fríd~áý_S~átúr~dáý".split("_"),weekdaysShort:"S~úñ_~Móñ_~Túé_~Wéd_~Thú_~Frí_~Sát".split("_"),weekdaysMin:"S~ú_Mó~_Tú_~Wé_T~h_Fr~_Sá".split("_"),weekdaysParseExact:!0,longDateFormat:{LT:"HH:mm",L:"DD/MM/YYYY",LL:"D MMMM YYYY",LLL:"D MMMM YYYY HH:mm",LLLL:"dddd, D MMMM YYYY HH:mm"},calendar:{sameDay:"[T~ódá~ý át] LT",nextDay:"[T~ómó~rró~w át] LT",nextWeek:"dddd [át] LT",lastDay:"[Ý~ést~érdá~ý át] LT",lastWeek:"[L~ást] dddd [át] LT",sameElse:"L"},relativeTime:{future:"í~ñ %s",past:"%s á~gó",s:"á ~féw ~sécó~ñds",ss:"%d s~écóñ~ds",m:"á ~míñ~úté",mm:"%d m~íñú~tés",h:"á~ñ hó~úr",hh:"%d h~óúrs",d:"á ~dáý",dd:"%d d~áýs",M:"á ~móñ~th",MM:"%d m~óñt~hs",y:"á ~ýéár",yy:"%d ý~éárs"},dayOfMonthOrdinalParse:/\d{1,2}(th|st|nd|rd)/,ordinal:function(t){var e=t%10,a=1===~~(t%100/10)?"th":1===e?"st":2===e?"nd":3===e?"rd":"th";return t+a},week:{dow:1,doy:4}});return e}))},fdbf:function(t,e,a){var n=a("4930");t.exports=n&&!Symbol.sham&&"symbol"==typeof Symbol.iterator},ffff:function(t,e,a){(function(t,e){e(a("c1df"))})(0,(function(t){"use strict";
|
||||||
//! moment.js locale configuration
|
//! moment.js locale configuration
|
||||||
var e=t.defineLocale("se",{months:"ođđajagemánnu_guovvamánnu_njukčamánnu_cuoŋománnu_miessemánnu_geassemánnu_suoidnemánnu_borgemánnu_čakčamánnu_golggotmánnu_skábmamánnu_juovlamánnu".split("_"),monthsShort:"ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_skáb_juov".split("_"),weekdays:"sotnabeaivi_vuossárga_maŋŋebárga_gaskavahkku_duorastat_bearjadat_lávvardat".split("_"),weekdaysShort:"sotn_vuos_maŋ_gask_duor_bear_láv".split("_"),weekdaysMin:"s_v_m_g_d_b_L".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"MMMM D. [b.] YYYY",LLL:"MMMM D. [b.] YYYY [ti.] HH:mm",LLLL:"dddd, MMMM D. [b.] YYYY [ti.] HH:mm"},calendar:{sameDay:"[otne ti] LT",nextDay:"[ihttin ti] LT",nextWeek:"dddd [ti] LT",lastDay:"[ikte ti] LT",lastWeek:"[ovddit] dddd [ti] LT",sameElse:"L"},relativeTime:{future:"%s geažes",past:"maŋit %s",s:"moadde sekunddat",ss:"%d sekunddat",m:"okta minuhta",mm:"%d minuhtat",h:"okta diimmu",hh:"%d diimmut",d:"okta beaivi",dd:"%d beaivvit",M:"okta mánnu",MM:"%d mánut",y:"okta jahki",yy:"%d jagit"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return e}))}}]);
|
var e=t.defineLocale("se",{months:"ođđajagemánnu_guovvamánnu_njukčamánnu_cuoŋománnu_miessemánnu_geassemánnu_suoidnemánnu_borgemánnu_čakčamánnu_golggotmánnu_skábmamánnu_juovlamánnu".split("_"),monthsShort:"ođđj_guov_njuk_cuo_mies_geas_suoi_borg_čakč_golg_skáb_juov".split("_"),weekdays:"sotnabeaivi_vuossárga_maŋŋebárga_gaskavahkku_duorastat_bearjadat_lávvardat".split("_"),weekdaysShort:"sotn_vuos_maŋ_gask_duor_bear_láv".split("_"),weekdaysMin:"s_v_m_g_d_b_L".split("_"),longDateFormat:{LT:"HH:mm",LTS:"HH:mm:ss",L:"DD.MM.YYYY",LL:"MMMM D. [b.] YYYY",LLL:"MMMM D. [b.] YYYY [ti.] HH:mm",LLLL:"dddd, MMMM D. [b.] YYYY [ti.] HH:mm"},calendar:{sameDay:"[otne ti] LT",nextDay:"[ihttin ti] LT",nextWeek:"dddd [ti] LT",lastDay:"[ikte ti] LT",lastWeek:"[ovddit] dddd [ti] LT",sameElse:"L"},relativeTime:{future:"%s geažes",past:"maŋit %s",s:"moadde sekunddat",ss:"%d sekunddat",m:"okta minuhta",mm:"%d minuhtat",h:"okta diimmu",hh:"%d diimmut",d:"okta beaivi",dd:"%d beaivvit",M:"okta mánnu",MM:"%d mánut",y:"okta jahki",yy:"%d jagit"},dayOfMonthOrdinalParse:/\d{1,2}\./,ordinal:"%d.",week:{dow:1,doy:4}});return e}))}}]);
|
||||||
//# sourceMappingURL=chunk-vendors.fad0ee6a.js.map
|
|
@ -34,6 +34,22 @@
|
|||||||
<label>{{ $t("webOptions.tagAuth") }}</label>
|
<label>{{ $t("webOptions.tagAuth") }}</label>
|
||||||
<b-form-input v-model="options.tag_auth" @change="update()"></b-form-input>
|
<b-form-input v-model="options.tag_auth" @change="update()"></b-form-input>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<h5>Auth0 options</h5>
|
||||||
|
<label>{{ $t("webOptions.auth0Audience") }}</label>
|
||||||
|
<b-form-input v-model="options.auth0_audience" @change="update()"></b-form-input>
|
||||||
|
|
||||||
|
<label>{{ $t("webOptions.auth0Domain") }}</label>
|
||||||
|
<b-form-input v-model="options.auth0_domain" @change="update()"></b-form-input>
|
||||||
|
|
||||||
|
<label>{{ $t("webOptions.auth0ClientId") }}</label>
|
||||||
|
<b-form-input v-model="options.auth0_client_id" @change="update()"></b-form-input>
|
||||||
|
|
||||||
|
<label>{{ $t("webOptions.auth0PublicKey") }}</label>
|
||||||
|
<b-textarea rows="10" v-model="options.auth0_public_key" @change="update()"></b-textarea>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -47,6 +47,7 @@ export default {
|
|||||||
|
|
||||||
selectJobs: "Select jobs",
|
selectJobs: "Select jobs",
|
||||||
webOptions: {
|
webOptions: {
|
||||||
|
title: "Web options",
|
||||||
esUrl: "Elasticsearch URL",
|
esUrl: "Elasticsearch URL",
|
||||||
esIndex: "Elasticsearch index name",
|
esIndex: "Elasticsearch index name",
|
||||||
esInsecure: "Do not verify SSL connections to Elasticsearch.",
|
esInsecure: "Do not verify SSL connections to Elasticsearch.",
|
||||||
|
@ -48,7 +48,7 @@
|
|||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<h4>{{ $t("jobOptions.title") }}</h4>
|
<h4>{{ $t("webOptions.title") }}</h4>
|
||||||
<b-card>
|
<b-card>
|
||||||
<WebOptions :options="frontend.web_options" :frontend-name="$route.params.name" @change="update()"></WebOptions>
|
<WebOptions :options="frontend.web_options" :frontend-name="$route.params.name" @change="update()"></WebOptions>
|
||||||
</b-card>
|
</b-card>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
publicPath: ""
|
publicPath: "",
|
||||||
|
filenameHashing: false,
|
||||||
|
productionSourceMap: false,
|
||||||
};
|
};
|
@ -37,6 +37,11 @@ class WebOptions(BaseModel):
|
|||||||
tagline: str = "Lightning-fast file system indexer and search tool"
|
tagline: str = "Lightning-fast file system indexer and search tool"
|
||||||
dev: bool = False
|
dev: bool = False
|
||||||
lang: str = "en"
|
lang: str = "en"
|
||||||
|
auth0_audience: str = None
|
||||||
|
auth0_domain: str = None
|
||||||
|
auth0_client_id: str = None
|
||||||
|
auth0_public_key: str = None
|
||||||
|
auth0_public_key_file: str = None
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
@ -45,6 +50,14 @@ class WebOptions(BaseModel):
|
|||||||
args = ["web", f"--es-url={self.es_url}", f"--bind={self.bind}",
|
args = ["web", f"--es-url={self.es_url}", f"--bind={self.bind}",
|
||||||
f"--tagline={self.tagline}", f"--lang={self.lang}"]
|
f"--tagline={self.tagline}", f"--lang={self.lang}"]
|
||||||
|
|
||||||
|
if self.auth0_audience:
|
||||||
|
args.append(f"--auth0-audience={self.auth0_audience}")
|
||||||
|
if self.auth0_domain:
|
||||||
|
args.append(f"--auth0-domain={self.auth0_domain}")
|
||||||
|
if self.auth0_client_id:
|
||||||
|
args.append(f"--auth0-client-id={self.auth0_client_id}")
|
||||||
|
if self.auth0_public_key_file:
|
||||||
|
args.append(f"--auth0-public-key-file={self.auth0_public_key_file}")
|
||||||
if self.es_insecure_ssl:
|
if self.es_insecure_ssl:
|
||||||
args.append(f"--es-insecure-ssl")
|
args.append(f"--es-insecure-ssl")
|
||||||
if self.auth:
|
if self.auth:
|
||||||
@ -283,6 +296,14 @@ class Sist2:
|
|||||||
# pipe_wrapper.close()
|
# pipe_wrapper.close()
|
||||||
|
|
||||||
def web(self, options: WebOptions, name: str):
|
def web(self, options: WebOptions, name: str):
|
||||||
|
|
||||||
|
if options.auth0_public_key:
|
||||||
|
with NamedTemporaryFile("w", prefix="sist2-admin", suffix=".txt", delete=False) as f:
|
||||||
|
f.write(options.auth0_public_key)
|
||||||
|
options.auth0_public_key_file = f.name
|
||||||
|
else:
|
||||||
|
options.auth0_public_key_file = None
|
||||||
|
|
||||||
args = [
|
args = [
|
||||||
self._bin_path,
|
self._bin_path,
|
||||||
*options.args()
|
*options.args()
|
||||||
|
8
sist2-vue/dist/css/chunk-vendors.css
vendored
Normal file
8
sist2-vue/dist/css/chunk-vendors.css
vendored
Normal file
File diff suppressed because one or more lines are too long
1
sist2-vue/dist/css/index.css
vendored
Normal file
1
sist2-vue/dist/css/index.css
vendored
Normal file
File diff suppressed because one or more lines are too long
33
sist2-vue/dist/index.html
vendored
33
sist2-vue/dist/index.html
vendored
@ -1,32 +1,3 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no"/><title>sist2</title><script defer="defer" src="js/chunk-vendors.js"></script><script defer="defer" src="js/index.js"></script><link href="css/chunk-vendors.css" rel="stylesheet"><link href="css/index.css" rel="stylesheet"></head><body><noscript><style>body {
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
||||||
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'/>
|
|
||||||
|
|
||||||
<title>sist2</title>
|
|
||||||
<link href="js/chunk-vendors.js" rel="preload" as="script"><link href="js/index.js" rel="preload" as="script"></head>
|
|
||||||
<body>
|
|
||||||
<noscript>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
height: initial;
|
height: initial;
|
||||||
}
|
}</style><div style="text-align: center; margin-top: 100px"><strong>We're sorry but sist2 doesn't work properly without JavaScript enabled. Please enable it to continue.</strong><br/><strong>Nous sommes désolés mais sist2 ne fonctionne pas correctement si JavaScript est activé. Veuillez l'activer pour continuer.</strong></div></noscript><div id="app"></div></body></html>
|
||||||
</style>
|
|
||||||
<div style="text-align: center; margin-top: 100px">
|
|
||||||
<strong>
|
|
||||||
We're sorry but sist2 doesn't work properly without JavaScript enabled.
|
|
||||||
Please enable it to continue.
|
|
||||||
</strong>
|
|
||||||
<br/>
|
|
||||||
<strong>
|
|
||||||
Nous sommes désolés mais sist2 ne fonctionne pas correctement
|
|
||||||
si JavaScript est activé.
|
|
||||||
Veuillez l'activer pour continuer.
|
|
||||||
</strong>
|
|
||||||
</div>
|
|
||||||
</noscript>
|
|
||||||
<div id="app"></div>
|
|
||||||
<script type="text/javascript" src="js/chunk-vendors.js"></script><script type="text/javascript" src="js/index.js"></script></body>
|
|
||||||
</html>
|
|
19501
sist2-vue/dist/js/chunk-vendors.js
vendored
19501
sist2-vue/dist/js/chunk-vendors.js
vendored
File diff suppressed because one or more lines are too long
4102
sist2-vue/dist/js/index.js
vendored
4102
sist2-vue/dist/js/index.js
vendored
File diff suppressed because one or more lines are too long
23299
sist2-vue/package-lock.json
generated
23299
sist2-vue/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -7,14 +7,15 @@
|
|||||||
"build": "vue-cli-service build --mode production"
|
"build": "vue-cli-service build --mode production"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@auth0/auth0-spa-js": "^2.0.2",
|
||||||
"@egjs/vue-infinitegrid": "3.3.0",
|
"@egjs/vue-infinitegrid": "3.3.0",
|
||||||
"axios": "^0.25.0",
|
"axios": "^0.25.0",
|
||||||
"bootstrap-vue": "^2.21.2",
|
"bootstrap-vue": "^2.21.2",
|
||||||
"core-js": "^3.6.5",
|
"core-js": "^3.6.5",
|
||||||
"d3": "^7.6.1",
|
"d3": "^5.6.1",
|
||||||
"date-fns": "^2.21.3",
|
"date-fns": "^2.21.3",
|
||||||
"dom-to-image": "^2.6.0",
|
"dom-to-image": "^2.6.0",
|
||||||
"fslightbox-vue": "file:../../../mnt/Hatchery/main/projects/sist2/fslightbox-vue-pro-1.3.1.tgz",
|
"fslightbox-vue": "file:../../../../mnt/Hatchery/projects/sist2/fslightbox-vue-pro-1.3.1.tgz",
|
||||||
"nouislider": "^15.2.0",
|
"nouislider": "^15.2.0",
|
||||||
"underscore": "^1.13.1",
|
"underscore": "^1.13.1",
|
||||||
"vue": "^2.6.12",
|
"vue": "^2.6.12",
|
||||||
@ -26,12 +27,12 @@
|
|||||||
"vuex": "^3.4.0"
|
"vuex": "^3.4.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/polyfill": "^7.11.5",
|
"@babel/polyfill": "^7.12.1",
|
||||||
"@vue/cli-plugin-babel": "~4.5.0",
|
"@vue/cli-plugin-babel": "~5.0.8",
|
||||||
"@vue/cli-plugin-router": "~4.5.0",
|
"@vue/cli-plugin-router": "~5.0.8",
|
||||||
"@vue/cli-plugin-typescript": "~4.5.0",
|
"@vue/cli-plugin-typescript": "^5.0.8",
|
||||||
"@vue/cli-plugin-vuex": "~4.5.0",
|
"@vue/cli-plugin-vuex": "~5.0.8",
|
||||||
"@vue/cli-service": "~4.5.0",
|
"@vue/cli-service": "^5.0.8",
|
||||||
"@vue/test-utils": "^1.0.3",
|
"@vue/test-utils": "^1.0.3",
|
||||||
"bootstrap": "^4.5.2",
|
"bootstrap": "^4.5.2",
|
||||||
"inspire-tree": "^4.3.1",
|
"inspire-tree": "^4.3.1",
|
||||||
|
@ -1,19 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div id="app" :class="getClass()">
|
<div id="app" :class="getClass()" v-if="!authLoading">
|
||||||
<NavBar></NavBar>
|
<NavBar></NavBar>
|
||||||
<router-view v-if="!configLoading"/>
|
<router-view v-if="!configLoading"/>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="loading-page" v-else>
|
||||||
|
<div class="loading-spinners">
|
||||||
|
<b-spinner type="grow" variant="primary"></b-spinner>
|
||||||
|
<b-spinner type="grow" variant="primary"></b-spinner>
|
||||||
|
<b-spinner type="grow" variant="primary"></b-spinner>
|
||||||
|
</div>
|
||||||
|
<div class="loading-text">
|
||||||
|
Loading • Chargement • 装载
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import NavBar from "@/components/NavBar";
|
import NavBar from "@/components/NavBar";
|
||||||
import {mapGetters} from "vuex";
|
import {mapActions, mapGetters, mapMutations} from "vuex";
|
||||||
|
import Sist2Api from "@/Sist2Api";
|
||||||
|
import {setupAuth0} from "@/main";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {NavBar},
|
components: {NavBar},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
configLoading: false
|
configLoading: false,
|
||||||
|
authLoading: true,
|
||||||
|
sist2InfoLoading: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@ -30,9 +44,43 @@ export default {
|
|||||||
this.configLoading = true;
|
this.configLoading = true;
|
||||||
window.setTimeout(() => this.configLoading = false, 10);
|
window.setTimeout(() => this.configLoading = false, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mutation.type === "setAuth0Token") {
|
||||||
|
this.authLoading = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Sist2Api.getSist2Info().then(data => {
|
||||||
|
|
||||||
|
if (data.auth0Enabled) {
|
||||||
|
this.authLoading = true;
|
||||||
|
setupAuth0(data.auth0Domain, data.auth0ClientId, data.auth0Audience)
|
||||||
|
|
||||||
|
this.$auth.$watch("loading", loading => {
|
||||||
|
if (loading === false) {
|
||||||
|
|
||||||
|
if (!this.$auth.isAuthenticated) {
|
||||||
|
this.$auth.loginWithRedirect();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove "code" param
|
||||||
|
window.history.replaceState({}, "", "/" + window.location.hash);
|
||||||
|
|
||||||
|
this.$store.dispatch("loadAuth0Token");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this.authLoading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setSist2Info(data);
|
||||||
|
this.setIndices(data.indices)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
...mapActions(["setSist2Info",]),
|
||||||
|
...mapMutations(["setIndices",]),
|
||||||
getClass() {
|
getClass() {
|
||||||
return {
|
return {
|
||||||
"theme-light": this.optTheme === "light",
|
"theme-light": this.optTheme === "light",
|
||||||
@ -314,4 +362,22 @@ mark {
|
|||||||
.pointer {
|
.pointer {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.loading-page {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
gap: 15px
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-spinners {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -9,13 +9,15 @@
|
|||||||
|
|
||||||
<span class="badge badge-pill version" v-if="$store && $store.state.sist2Info">
|
<span class="badge badge-pill version" v-if="$store && $store.state.sist2Info">
|
||||||
v{{ sist2Version() }}<span v-if="isDebug()">-dbg</span><span v-if="isLegacy() && !hideLegacy()">-<a
|
v{{ sist2Version() }}<span v-if="isDebug()">-dbg</span><span v-if="isLegacy() && !hideLegacy()">-<a
|
||||||
href="https://github.com/simon987/sist2/blob/master/docs/USAGE.md#elasticsearch" target="_blank">legacyES</a></span>
|
href="https://github.com/simon987/sist2/blob/master/docs/USAGE.md#elasticsearch"
|
||||||
|
target="_blank">legacyES</a></span>
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span v-if="$store && $store.state.sist2Info" class="tagline" v-html="tagline()"></span>
|
<span v-if="$store && $store.state.sist2Info" class="tagline" v-html="tagline()"></span>
|
||||||
|
|
||||||
<b-button class="ml-auto" to="stats" variant="link">{{ $t("stats") }}</b-button>
|
<b-button class="ml-auto" to="stats" variant="link">{{ $t("stats") }}</b-button>
|
||||||
<b-button to="config" variant="link">{{ $t("config") }}</b-button>
|
<b-button to="config" variant="link">{{ $t("config") }}</b-button>
|
||||||
|
<b-button v-if="$auth && $auth.isAuthenticated" variant="link" @click="onLogoutClick()">logout</b-button>
|
||||||
</b-navbar>
|
</b-navbar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -40,6 +42,9 @@ export default {
|
|||||||
},
|
},
|
||||||
hideLegacy() {
|
hideLegacy() {
|
||||||
return this.$store.state.optHideLegacy;
|
return this.$store.state.optHideLegacy;
|
||||||
|
},
|
||||||
|
onLogoutClick() {
|
||||||
|
this.$auth.logout();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,8 +335,8 @@ export default {
|
|||||||
indexPicker: {
|
indexPicker: {
|
||||||
selectNone: "Sélectionner aucun",
|
selectNone: "Sélectionner aucun",
|
||||||
selectAll: "Sélectionner tout",
|
selectAll: "Sélectionner tout",
|
||||||
selectedIndex: "indice sélectionné",
|
selectedIndex: "index sélectionné",
|
||||||
selectedIndices: "indices sélectionnés",
|
selectedIndices: "index sélectionnés",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"zh-CN": {
|
"zh-CN": {
|
||||||
|
@ -3,16 +3,32 @@ import 'mutationobserver-shim'
|
|||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import './plugins/bootstrap-vue'
|
import './plugins/bootstrap-vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import router from './router'
|
import router, {setUseAuth0} from './router'
|
||||||
import store from './store'
|
import store from './store'
|
||||||
import VueI18n from "vue-i18n";
|
import VueI18n from "vue-i18n";
|
||||||
import messages from "@/i18n/messages";
|
import messages from "@/i18n/messages";
|
||||||
|
import { Auth0Plugin } from './plugins/auth0';
|
||||||
|
|
||||||
import VueRouter from "vue-router";
|
import VueRouter from "vue-router";
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
|
export function setupAuth0(domain, clientId, audience) {
|
||||||
|
|
||||||
|
setUseAuth0(true);
|
||||||
|
|
||||||
|
Vue.use(Auth0Plugin, {
|
||||||
|
domain,
|
||||||
|
clientId,
|
||||||
|
audience,
|
||||||
|
onRedirectCallback: appState => {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Vue.prototype.$auth = null;
|
||||||
|
|
||||||
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
Vue.use(VueI18n);
|
Vue.use(VueI18n);
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
|
|
||||||
|
138
sist2-vue/src/plugins/auth0.js
Normal file
138
sist2-vue/src/plugins/auth0.js
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
import Vue from 'vue';
|
||||||
|
import {createAuth0Client} from '@auth0/auth0-spa-js';
|
||||||
|
|
||||||
|
/** Define a default action to perform after authentication */
|
||||||
|
const DEFAULT_REDIRECT_CALLBACK = () =>
|
||||||
|
window.history.replaceState({}, document.title, window.location.pathname);
|
||||||
|
|
||||||
|
let instance;
|
||||||
|
|
||||||
|
/** Returns the current instance of the SDK */
|
||||||
|
export const getInstance = () => instance;
|
||||||
|
|
||||||
|
/** Creates an instance of the Auth0 SDK. If one has already been created, it returns that instance */
|
||||||
|
export const useAuth0 = ({
|
||||||
|
domain, clientId, audience,
|
||||||
|
onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
|
||||||
|
redirectUri = window.location.origin,
|
||||||
|
}) => {
|
||||||
|
if (instance) return instance;
|
||||||
|
|
||||||
|
// The 'instance' is simply a Vue object
|
||||||
|
instance = new Vue({
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
loading: true,
|
||||||
|
isAuthenticated: false,
|
||||||
|
user: {},
|
||||||
|
auth0Client: null,
|
||||||
|
popupOpen: false,
|
||||||
|
error: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/** Authenticates the user using a popup window */
|
||||||
|
async loginWithPopup(options, config) {
|
||||||
|
this.popupOpen = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.auth0Client.loginWithPopup(options, config);
|
||||||
|
this.user = await this.auth0Client.getUser();
|
||||||
|
this.isAuthenticated = await this.auth0Client.isAuthenticated();
|
||||||
|
this.error = null;
|
||||||
|
} catch (e) {
|
||||||
|
this.error = e;
|
||||||
|
// eslint-disable-next-line
|
||||||
|
console.error(e);
|
||||||
|
} finally {
|
||||||
|
this.popupOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.user = await this.auth0Client.getUser();
|
||||||
|
this.isAuthenticated = true;
|
||||||
|
},
|
||||||
|
/** Handles the callback when logging in using a redirect */
|
||||||
|
async handleRedirectCallback() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
await this.auth0Client.handleRedirectCallback();
|
||||||
|
this.user = await this.auth0Client.getUser();
|
||||||
|
this.isAuthenticated = true;
|
||||||
|
this.error = null;
|
||||||
|
} catch (e) {
|
||||||
|
this.error = e;
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** Authenticates the user using the redirect method */
|
||||||
|
loginWithRedirect(o) {
|
||||||
|
return this.auth0Client.loginWithRedirect(o);
|
||||||
|
},
|
||||||
|
/** Returns all the claims present in the ID token */
|
||||||
|
getIdTokenClaims(o) {
|
||||||
|
return this.auth0Client.getIdTokenClaims(o);
|
||||||
|
},
|
||||||
|
/** Returns the access token. If the token is invalid or missing, a new one is retrieved */
|
||||||
|
getTokenSilently(o) {
|
||||||
|
return this.auth0Client.getTokenSilently(o);
|
||||||
|
},
|
||||||
|
|
||||||
|
/** Gets the access token using a popup window */
|
||||||
|
|
||||||
|
getTokenWithPopup(o) {
|
||||||
|
return this.auth0Client.getTokenWithPopup(o);
|
||||||
|
},
|
||||||
|
/** Logs the user out and removes their session on the authorization server */
|
||||||
|
logout() {
|
||||||
|
return this.auth0Client.logout({ logoutParams: { returnTo: window.location.origin } });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** Use this lifecycle method to instantiate the SDK client */
|
||||||
|
async created() {
|
||||||
|
// Create a new instance of the SDK client using members of the given options object
|
||||||
|
this.auth0Client = await createAuth0Client({
|
||||||
|
domain: domain,
|
||||||
|
clientId: clientId,
|
||||||
|
|
||||||
|
authorizationParams: {
|
||||||
|
redirect_uri: redirectUri,
|
||||||
|
audience: audience,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
// If the user is returning to the app after authentication..
|
||||||
|
if (
|
||||||
|
window.location.search.includes('code=') &&
|
||||||
|
window.location.search.includes('state=')
|
||||||
|
) {
|
||||||
|
// handle the redirect and retrieve tokens
|
||||||
|
const {appState} = await this.auth0Client.handleRedirectCallback();
|
||||||
|
|
||||||
|
this.error = null;
|
||||||
|
|
||||||
|
// Notify subscribers that the redirect callback has happened, passing the appState
|
||||||
|
// (useful for retrieving any pre-authentication state)
|
||||||
|
onRedirectCallback(appState);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.error = e;
|
||||||
|
} finally {
|
||||||
|
// Initialize our internal authentication state
|
||||||
|
this.isAuthenticated = await this.auth0Client.isAuthenticated();
|
||||||
|
this.user = await this.auth0Client.getUser();
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create a simple Vue plugin to expose the wrapper object throughout the application
|
||||||
|
export const Auth0Plugin = {
|
||||||
|
install(Vue, options) {
|
||||||
|
Vue.prototype.$auth = useAuth0(options);
|
||||||
|
}
|
||||||
|
};
|
28
sist2-vue/src/router/auth0.ts
Normal file
28
sist2-vue/src/router/auth0.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import {getInstance} from "@/plugins/auth0";
|
||||||
|
|
||||||
|
export const authGuard = (to, from, next) => {
|
||||||
|
|
||||||
|
const authService = getInstance();
|
||||||
|
|
||||||
|
const fn = () => {
|
||||||
|
// If the user is authenticated, continue with the route
|
||||||
|
if (authService.isAuthenticated) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, log in
|
||||||
|
authService.loginWithRedirect({appState: {targetUrl: to.fullPath}});
|
||||||
|
};
|
||||||
|
|
||||||
|
// If loading has already finished, check our auth state using `fn()`
|
||||||
|
if (!authService.loading) {
|
||||||
|
return fn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watch for the loading property to change before we check isAuthenticated
|
||||||
|
authService.$watch("loading", loading => {
|
||||||
|
if (loading === false) {
|
||||||
|
return fn();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@ -4,14 +4,29 @@ import StatsPage from "../views/StatsPage.vue"
|
|||||||
import Configuration from "../views/Configuration.vue"
|
import Configuration from "../views/Configuration.vue"
|
||||||
import SearchPage from "@/views/SearchPage.vue";
|
import SearchPage from "@/views/SearchPage.vue";
|
||||||
import FilePage from "@/views/FilePage.vue";
|
import FilePage from "@/views/FilePage.vue";
|
||||||
|
import {authGuard as auth0AuthGuard} from "@/router/auth0";
|
||||||
|
|
||||||
Vue.use(VueRouter)
|
Vue.use(VueRouter)
|
||||||
|
|
||||||
|
let USE_AUTH0 = false
|
||||||
|
export function setUseAuth0(val) {
|
||||||
|
USE_AUTH0 = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
const authGuard = (to, from, next) => {
|
||||||
|
if (USE_AUTH0) {
|
||||||
|
return auth0AuthGuard(to, from, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
const routes: Array<RouteConfig> = [
|
const routes: Array<RouteConfig> = [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
name: "SearchPage",
|
name: "SearchPage",
|
||||||
component: SearchPage
|
component: SearchPage,
|
||||||
|
beforeEnter: authGuard
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/stats",
|
path: "/stats",
|
||||||
@ -34,7 +49,7 @@ const router = new VueRouter({
|
|||||||
mode: "hash",
|
mode: "hash",
|
||||||
base: process.env.BASE_URL,
|
base: process.env.BASE_URL,
|
||||||
routes,
|
routes,
|
||||||
scrollBehavior (to, from, savedPosition) {
|
scrollBehavior(to, from, savedPosition) {
|
||||||
// return desired position
|
// return desired position
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -3,6 +3,7 @@ import Vuex from "vuex"
|
|||||||
import VueRouter, {Route} from "vue-router";
|
import VueRouter, {Route} from "vue-router";
|
||||||
import {EsHit, EsResult, EsTag, Index, Tag} from "@/Sist2Api";
|
import {EsHit, EsResult, EsTag, Index, Tag} from "@/Sist2Api";
|
||||||
import {deserializeMimes, randomSeed, serializeMimes} from "@/util";
|
import {deserializeMimes, randomSeed, serializeMimes} from "@/util";
|
||||||
|
import {getInstance} from "@/plugins/auth0.js";
|
||||||
|
|
||||||
const CONF_VERSION = 2;
|
const CONF_VERSION = 2;
|
||||||
|
|
||||||
@ -82,7 +83,9 @@ export default new Vuex.Store({
|
|||||||
uiDetailsMimeAgg: null,
|
uiDetailsMimeAgg: null,
|
||||||
uiShowDetails: false,
|
uiShowDetails: false,
|
||||||
|
|
||||||
uiMimeMap: [] as any[]
|
uiMimeMap: [] as any[],
|
||||||
|
|
||||||
|
auth0Token: null
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
setUiShowDetails: (state, val) => state.uiShowDetails = val,
|
setUiShowDetails: (state, val) => state.uiShowDetails = val,
|
||||||
@ -188,6 +191,7 @@ export default new Vuex.Store({
|
|||||||
busTnTouchStart: (doc_id) => {
|
busTnTouchStart: (doc_id) => {
|
||||||
// noop
|
// noop
|
||||||
},
|
},
|
||||||
|
setAuth0Token: (state, val) => state.auth0Token = val,
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
setSist2Info: (store, val) => {
|
setSist2Info: (store, val) => {
|
||||||
@ -332,6 +336,14 @@ export default new Vuex.Store({
|
|||||||
commit("setUiLightboxCaptions", []);
|
commit("setUiLightboxCaptions", []);
|
||||||
commit("setUiLightboxKey", 0);
|
commit("setUiLightboxKey", 0);
|
||||||
commit("setUiDetailsMimeAgg", null);
|
commit("setUiDetailsMimeAgg", null);
|
||||||
|
},
|
||||||
|
async loadAuth0Token({commit}) {
|
||||||
|
const authService = getInstance();
|
||||||
|
|
||||||
|
const accessToken = await authService.getTokenSilently()
|
||||||
|
commit("setAuth0Token", accessToken);
|
||||||
|
|
||||||
|
document.cookie = `sist2-auth0=${accessToken};`;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modules: {},
|
modules: {},
|
||||||
|
@ -153,7 +153,7 @@ export default {
|
|||||||
components: {LanguageIcon, GearIcon, DebugInfo, Preloader},
|
components: {LanguageIcon, GearIcon, DebugInfo, Preloader},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: true,
|
loading: false,
|
||||||
configLoading: false,
|
configLoading: false,
|
||||||
langOptions: [
|
langOptions: [
|
||||||
{value: "en", text: this.$t("lang.en")},
|
{value: "en", text: this.$t("lang.en")},
|
||||||
@ -257,11 +257,6 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
sist2.getSist2Info().then(data => {
|
|
||||||
this.setSist2Info(data);
|
|
||||||
this.loading = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
this.$store.subscribe((mutation) => {
|
this.$store.subscribe((mutation) => {
|
||||||
if (mutation.type.startsWith("setOpt")) {
|
if (mutation.type.startsWith("setOpt")) {
|
||||||
this.$store.dispatch("updateConfiguration");
|
this.$store.dispatch("updateConfiguration");
|
||||||
|
@ -107,13 +107,6 @@ export default Vue.extend({
|
|||||||
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.$store.state.sist2Info === null) {
|
|
||||||
sist2.getSist2Info().then(data => {
|
|
||||||
this.$store.dispatch("setSist2Info", data);
|
|
||||||
this.$store.commit("setIndices", data.indices);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let query = null;
|
let query = null;
|
||||||
if (this.$route.query.byId) {
|
if (this.$route.query.byId) {
|
||||||
query = this.findById(this.$route.query.byId);
|
query = this.findById(this.$route.query.byId);
|
||||||
|
@ -130,25 +130,18 @@ export default Vue.extend({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.setIndices(this.$store.getters["sist2Info"].indices)
|
||||||
|
|
||||||
this.getDateRange().then((range: { min: number, max: number }) => {
|
this.getDateRange().then((range: { min: number, max: number }) => {
|
||||||
this.setDateBoundsMin(range.min);
|
this.setDateBoundsMin(range.min);
|
||||||
this.setDateBoundsMax(range.max);
|
this.setDateBoundsMax(range.max);
|
||||||
|
|
||||||
sist2.getSist2Info().then(data => {
|
const doBlankSearch = !this.$store.state.optUpdateMimeMap;
|
||||||
this.setSist2Info(data);
|
|
||||||
this.setIndices(data.indices);
|
|
||||||
|
|
||||||
const doBlankSearch = !this.$store.state.optUpdateMimeMap;
|
Sist2Api.getMimeTypes(Sist2Query.searchQuery(doBlankSearch)).then(({mimeMap}) => {
|
||||||
|
this.$store.commit("setUiMimeMap", mimeMap);
|
||||||
Sist2Api.getMimeTypes(Sist2Query.searchQuery(doBlankSearch)).then(({mimeMap}) => {
|
this.uiLoading = false;
|
||||||
this.$store.commit("setUiMimeMap", mimeMap);
|
this.search(true);
|
||||||
this.uiLoading = false;
|
|
||||||
this.search(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
}).catch(() => {
|
|
||||||
this.showErrorToast();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
48
src/auth0/auth0_c_api.cpp
Normal file
48
src/auth0/auth0_c_api.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include "auth0_c_api.h"
|
||||||
|
#include "jwt/jwt.hpp"
|
||||||
|
#include "iostream"
|
||||||
|
#include "cjson/cJSON.h"
|
||||||
|
|
||||||
|
int auth0_verify_jwt(const char *secret_str, const char *token, const char *audience) {
|
||||||
|
|
||||||
|
using namespace jwt::params;
|
||||||
|
|
||||||
|
jwt::jwt_object object;
|
||||||
|
try {
|
||||||
|
object = jwt::decode(
|
||||||
|
token,
|
||||||
|
algorithms({"RS256"}),
|
||||||
|
secret(secret_str),
|
||||||
|
verify(true)
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (const jwt::TokenExpiredError& e) {
|
||||||
|
return AUTH0_ERR_EXPIRED;
|
||||||
|
} catch (const jwt::SignatureFormatError& e) {
|
||||||
|
return AUTH0_ERR_SIG_FORMAT;
|
||||||
|
} catch (const jwt::DecodeError& e) {
|
||||||
|
return AUTH0_ERR_DECODE;
|
||||||
|
} catch (const jwt::VerificationError& e) {
|
||||||
|
return AUTH0_ERR_VERIFICATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream buf;
|
||||||
|
buf << object.payload();
|
||||||
|
std::string json_payload_str = buf.str();
|
||||||
|
cJSON *payload = cJSON_Parse(json_payload_str.c_str());
|
||||||
|
|
||||||
|
bool audience_ok = false;
|
||||||
|
cJSON *aud;
|
||||||
|
cJSON_ArrayForEach(aud, cJSON_GetObjectItem(payload, "aud")) {
|
||||||
|
if (aud != nullptr && strcmp(aud->valuestring, audience) == 0) {
|
||||||
|
audience_ok = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cJSON_Delete(payload);
|
||||||
|
|
||||||
|
if (!audience_ok) {
|
||||||
|
return AUTH0_ERR_AUDIENCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AUTH0_OK;
|
||||||
|
}
|
21
src/auth0/auth0_c_api.h
Normal file
21
src/auth0/auth0_c_api.h
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#ifndef SIST2_AUTH0_C_API_H
|
||||||
|
#define SIST2_AUTH0_C_API_H
|
||||||
|
|
||||||
|
#include "stdlib.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
#define EXTERNC extern "C"
|
||||||
|
#else
|
||||||
|
#define EXTERNC
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define AUTH0_OK (0)
|
||||||
|
#define AUTH0_ERR_EXPIRED (1)
|
||||||
|
#define AUTH0_ERR_SIG_FORMAT (2)
|
||||||
|
#define AUTH0_ERR_DECODE (3)
|
||||||
|
#define AUTH0_ERR_VERIFICATION (4)
|
||||||
|
#define AUTH0_ERR_AUDIENCE (5)
|
||||||
|
|
||||||
|
EXTERNC int auth0_verify_jwt(const char *secret, const char *token, const char* audience);
|
||||||
|
|
||||||
|
#endif
|
31
src/cli.c
31
src/cli.c
@ -501,6 +501,37 @@ int web_args_validate(web_args_t *args, int argc, const char **argv) {
|
|||||||
args->tag_auth_enabled = FALSE;
|
args->tag_auth_enabled = FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (args->auth0_public_key_path != NULL || args->auth0_audience != NULL || args->auth0_client_id ||
|
||||||
|
args->auth0_domain) {
|
||||||
|
|
||||||
|
if (args->auth0_public_key_path == NULL) {
|
||||||
|
fprintf(stderr, "Missing --auth0-public-key-file argument");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (args->auth0_audience == NULL) {
|
||||||
|
fprintf(stderr, "Missing --auth0-audience argument");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (args->auth0_client_id == NULL) {
|
||||||
|
fprintf(stderr, "Missing --auth0-client-id argument");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (args->auth0_domain == NULL) {
|
||||||
|
fprintf(stderr, "Missing --auth0-domain argument");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args->auth0_public_key_path != NULL) {
|
||||||
|
if (load_external_file(args->auth0_public_key_path, &args->auth0_public_key) != 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
args->auth0_enabled = TRUE;
|
||||||
|
} else {
|
||||||
|
args->auth0_enabled = FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
args->index_count = argc - 1;
|
args->index_count = argc - 1;
|
||||||
args->indices = argv + 1;
|
args->indices = argv + 1;
|
||||||
|
|
||||||
|
@ -77,6 +77,12 @@ typedef struct web_args {
|
|||||||
char *lang;
|
char *lang;
|
||||||
char auth_user[256];
|
char auth_user[256];
|
||||||
char auth_pass[256];
|
char auth_pass[256];
|
||||||
|
int auth0_enabled;
|
||||||
|
char *auth0_audience;
|
||||||
|
char *auth0_domain;
|
||||||
|
char *auth0_client_id;
|
||||||
|
char *auth0_public_key_path;
|
||||||
|
char *auth0_public_key;
|
||||||
int auth_enabled;
|
int auth_enabled;
|
||||||
int tag_auth_enabled;
|
int tag_auth_enabled;
|
||||||
int index_count;
|
int index_count;
|
||||||
|
@ -105,6 +105,13 @@ typedef struct {
|
|||||||
char *auth_pass;
|
char *auth_pass;
|
||||||
int auth_enabled;
|
int auth_enabled;
|
||||||
int tag_auth_enabled;
|
int tag_auth_enabled;
|
||||||
|
|
||||||
|
int auth0_enabled;
|
||||||
|
char *auth0_public_key;
|
||||||
|
char *auth0_audience;
|
||||||
|
char *auth0_domain;
|
||||||
|
char *auth0_client_id;
|
||||||
|
|
||||||
char *tagline;
|
char *tagline;
|
||||||
struct index_t indices[256];
|
struct index_t indices[256];
|
||||||
char lang[10];
|
char lang[10];
|
||||||
|
10
src/main.c
10
src/main.c
@ -13,6 +13,7 @@
|
|||||||
#include "web/serve.h"
|
#include "web/serve.h"
|
||||||
#include "parsing/mime.h"
|
#include "parsing/mime.h"
|
||||||
#include "parsing/parse.h"
|
#include "parsing/parse.h"
|
||||||
|
#include "auth0/auth0_c_api.h"
|
||||||
|
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
@ -562,6 +563,11 @@ void sist2_web(web_args_t *args) {
|
|||||||
WebCtx.tag_auth_enabled = args->tag_auth_enabled;
|
WebCtx.tag_auth_enabled = args->tag_auth_enabled;
|
||||||
WebCtx.tagline = args->tagline;
|
WebCtx.tagline = args->tagline;
|
||||||
WebCtx.dev = args->dev;
|
WebCtx.dev = args->dev;
|
||||||
|
WebCtx.auth0_enabled = args->auth0_enabled;
|
||||||
|
WebCtx.auth0_public_key = args->auth0_public_key;
|
||||||
|
WebCtx.auth0_client_id = args->auth0_client_id;
|
||||||
|
WebCtx.auth0_domain = args->auth0_domain;
|
||||||
|
WebCtx.auth0_audience = args->auth0_audience;
|
||||||
strcpy(WebCtx.lang, args->lang);
|
strcpy(WebCtx.lang, args->lang);
|
||||||
|
|
||||||
for (int i = 0; i < args->index_count; i++) {
|
for (int i = 0; i < args->index_count; i++) {
|
||||||
@ -711,6 +717,10 @@ int main(int argc, const char *argv[]) {
|
|||||||
OPT_STRING(0, "es-index", &common_es_index, "Elasticsearch index name. DEFAULT=sist2"),
|
OPT_STRING(0, "es-index", &common_es_index, "Elasticsearch index name. DEFAULT=sist2"),
|
||||||
OPT_STRING(0, "bind", &web_args->listen_address, "Listen on this address. DEFAULT=localhost:4090"),
|
OPT_STRING(0, "bind", &web_args->listen_address, "Listen on this address. DEFAULT=localhost:4090"),
|
||||||
OPT_STRING(0, "auth", &web_args->credentials, "Basic auth in user:password format"),
|
OPT_STRING(0, "auth", &web_args->credentials, "Basic auth in user:password format"),
|
||||||
|
OPT_STRING(0, "auth0-audience", &web_args->auth0_audience, "API audience/identifier"),
|
||||||
|
OPT_STRING(0, "auth0-domain", &web_args->auth0_domain, "Application domain"),
|
||||||
|
OPT_STRING(0, "auth0-client-id", &web_args->auth0_client_id, "Application client ID"),
|
||||||
|
OPT_STRING(0, "auth0-public-key-file", &web_args->auth0_public_key_path, "Path to Auth0 public key file extracted from <domain>/pem"),
|
||||||
OPT_STRING(0, "tag-auth", &web_args->tag_credentials, "Basic auth in user:password format for tagging"),
|
OPT_STRING(0, "tag-auth", &web_args->tag_credentials, "Basic auth in user:password format for tagging"),
|
||||||
OPT_STRING(0, "tagline", &web_args->tagline, "Tagline in navbar"),
|
OPT_STRING(0, "tagline", &web_args->tagline, "Tagline in navbar"),
|
||||||
OPT_BOOLEAN(0, "dev", &web_args->dev, "Serve html & js files from disk (for development)"),
|
OPT_BOOLEAN(0, "dev", &web_args->dev, "Serve html & js files from disk (for development)"),
|
||||||
|
@ -49,7 +49,7 @@
|
|||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "git_hash.h"
|
#include "git_hash.h"
|
||||||
|
|
||||||
#define VERSION "2.13.0"
|
#define VERSION "2.13.1"
|
||||||
static const char *const Version = VERSION;
|
static const char *const Version = VERSION;
|
||||||
|
|
||||||
#ifndef SIST_PLATFORM
|
#ifndef SIST_PLATFORM
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include "static_generated.c"
|
#include "static_generated.c"
|
||||||
#include "src/index/elastic.h"
|
#include "src/index/elastic.h"
|
||||||
#include "src/index/web.h"
|
#include "src/index/web.h"
|
||||||
|
#include "src/auth0/auth0_c_api.h"
|
||||||
|
|
||||||
#include <src/ctx.h>
|
#include <src/ctx.h>
|
||||||
|
|
||||||
@ -342,6 +343,14 @@ void index_info(struct mg_connection *nc) {
|
|||||||
cJSON_AddStringToObject(json, "sist2Hash", Sist2CommitHash);
|
cJSON_AddStringToObject(json, "sist2Hash", Sist2CommitHash);
|
||||||
cJSON_AddStringToObject(json, "lang", WebCtx.lang);
|
cJSON_AddStringToObject(json, "lang", WebCtx.lang);
|
||||||
cJSON_AddBoolToObject(json, "dev", WebCtx.dev);
|
cJSON_AddBoolToObject(json, "dev", WebCtx.dev);
|
||||||
|
|
||||||
|
cJSON_AddBoolToObject(json, "auth0Enabled", WebCtx.auth0_enabled);
|
||||||
|
if (WebCtx.auth0_enabled) {
|
||||||
|
cJSON_AddStringToObject(json, "auth0Domain", WebCtx.auth0_domain);
|
||||||
|
cJSON_AddStringToObject(json, "auth0ClientId", WebCtx.auth0_client_id);
|
||||||
|
cJSON_AddStringToObject(json, "auth0Audience", WebCtx.auth0_audience);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SIST_DEBUG
|
#ifdef SIST_DEBUG
|
||||||
cJSON_AddBoolToObject(json, "debug", TRUE);
|
cJSON_AddBoolToObject(json, "debug", TRUE);
|
||||||
#else
|
#else
|
||||||
@ -588,6 +597,42 @@ int validate_auth(struct mg_connection *nc, struct mg_http_message *hm) {
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int check_auth0(struct mg_http_message *hm) {
|
||||||
|
|
||||||
|
struct mg_str *cookie = mg_http_get_header(hm, "Cookie");
|
||||||
|
if (cookie == NULL) {
|
||||||
|
LOG_WARNING("serve.c", "Unauthorized request (no auth cookie)")
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mg_str token = mg_str("");
|
||||||
|
char *token_str = NULL;
|
||||||
|
|
||||||
|
token = mg_http_get_header_var(*cookie, mg_str("sist2-auth0"));
|
||||||
|
if (token.len == 0) {
|
||||||
|
LOG_WARNING("serve.c", "Unauthorized request (no auth cookie)")
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
token_str = malloc(token.len + 1);
|
||||||
|
strncpy(token_str, token.ptr, token.len);
|
||||||
|
*(token_str + token.len) = '\0';
|
||||||
|
|
||||||
|
int res = auth0_verify_jwt(
|
||||||
|
WebCtx.auth0_public_key,
|
||||||
|
token_str,
|
||||||
|
WebCtx.auth0_audience
|
||||||
|
);
|
||||||
|
free(token_str);
|
||||||
|
|
||||||
|
if (res != AUTH0_OK) {
|
||||||
|
LOG_WARNINGF("serve.c", "Unauthorized request (JWT validation error: %d)", res);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
static void ev_router(struct mg_connection *nc, int ev, void *ev_data, UNUSED(void *fn_data)) {
|
static void ev_router(struct mg_connection *nc, int ev, void *ev_data, UNUSED(void *fn_data)) {
|
||||||
|
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
@ -606,20 +651,34 @@ static void ev_router(struct mg_connection *nc, int ev, void *ev_data, UNUSED(vo
|
|||||||
|
|
||||||
if (mg_http_match_uri(hm, "/")) {
|
if (mg_http_match_uri(hm, "/")) {
|
||||||
search_index(nc, hm);
|
search_index(nc, hm);
|
||||||
|
return;
|
||||||
} else if (mg_http_match_uri(hm, "/favicon.ico")) {
|
} else if (mg_http_match_uri(hm, "/favicon.ico")) {
|
||||||
favicon(nc, hm);
|
favicon(nc, hm);
|
||||||
|
return;
|
||||||
} else if (mg_http_match_uri(hm, "/css/index.css")) {
|
} else if (mg_http_match_uri(hm, "/css/index.css")) {
|
||||||
style(nc, hm);
|
style(nc, hm);
|
||||||
|
return;
|
||||||
} else if (mg_http_match_uri(hm, "/css/chunk-vendors.css")) {
|
} else if (mg_http_match_uri(hm, "/css/chunk-vendors.css")) {
|
||||||
style_vendor(nc, hm);
|
style_vendor(nc, hm);
|
||||||
|
return;
|
||||||
} else if (mg_http_match_uri(hm, "/js/index.js")) {
|
} else if (mg_http_match_uri(hm, "/js/index.js")) {
|
||||||
javascript(nc, hm);
|
javascript(nc, hm);
|
||||||
|
return;
|
||||||
} else if (mg_http_match_uri(hm, "/js/chunk-vendors.js")) {
|
} else if (mg_http_match_uri(hm, "/js/chunk-vendors.js")) {
|
||||||
javascript_vendor(nc, hm);
|
javascript_vendor(nc, hm);
|
||||||
} else if (mg_http_match_uri(hm, "/es")) {
|
return;
|
||||||
search(nc, hm);
|
|
||||||
} else if (mg_http_match_uri(hm, "/i")) {
|
} else if (mg_http_match_uri(hm, "/i")) {
|
||||||
index_info(nc);
|
index_info(nc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WebCtx.auth0_enabled && !check_auth0(hm)) {
|
||||||
|
mg_http_reply(nc, 403, HTTP_SERVER_HEADER HTTP_TEXT_TYPE_HEADER, "Unauthorized (auth0 error)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mg_http_match_uri(hm, "/es")) {
|
||||||
|
search(nc, hm);
|
||||||
} else if (mg_http_match_uri(hm, "/status")) {
|
} else if (mg_http_match_uri(hm, "/status")) {
|
||||||
status(nc);
|
status(nc);
|
||||||
} else if (mg_http_match_uri(hm, "/f/*")) {
|
} else if (mg_http_match_uri(hm, "/f/*")) {
|
||||||
|
10
src/web/static_generated.c
vendored
10
src/web/static_generated.c
vendored
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user