From 6b30fa26d93dc266c145593e83e4fea7166e8910 Mon Sep 17 00:00:00 2001 From: ymatienzo Date: Wed, 30 Jun 2021 14:40:07 +0200 Subject: [PATCH] Auth0 integration_V2 (#703) (closes #706) * add separate auth0 file for beckend * revert commit * configured auth0 on GUI * added interceptor * added support for accepting GUI config file through CLI on server command * fixed not rendering component on async token call * finalize Auth0 Implementation * removed unused code * fixing xdg-open not found bug on some linux-machine * trying to fix continuous reloading issue when deploying on k8s * auth0-implemention * auth0-implemention * updated readme.md * auth0-integration * Delete auth0-config.json * Auth0 Implementation * Auth0 integration V2 * Updated interceptor.js updated interceptor to create a new header for a request which doesn't have one. * Update serverMetadata.js reverted back the changes to this file. * Update version.js reverted this file back to its original state Co-authored-by: sanj singh <36148196+sanjsingh07@users.noreply.github.com> Co-authored-by: Sanj Co-authored-by: OrunPay B.V --- README.md | 8 ++ cmd/server_amd64.go | 32 +++++++ examples/configs/trader/sample_GUI_config.cfg | 11 +++ glide.lock | 4 + glide.yaml | 4 + gui/backend/api_server.go | 4 + gui/backend/jwt_middleware.go | 88 +++++++++++++++++++ gui/backend/routes.go | 37 ++++---- gui/backend/serverMetadata.go | 3 + gui/web/package.json | 2 + gui/web/src/App.js | 28 +++++- .../src/components/molecules/Header/Header.js | 3 + .../screens/LogAuth/LoginRedirect.js | 41 +++++++++ .../screens/LogAuth/LogoutButton.js | 14 +++ gui/web/src/kelp-ops-api/getUserData.js | 1 + gui/web/src/kelp-ops-api/interceptor.js | 32 +++++++ gui/web/src/kelp-ops-api/serverMetadata.js | 2 +- gui/web/src/kelp-ops-api/version.js | 2 +- gui/web/yarn.lock | 67 ++++++++++++++ support/guiconfig/config.go | 24 +++++ 20 files changed, 386 insertions(+), 21 deletions(-) create mode 100644 examples/configs/trader/sample_GUI_config.cfg create mode 100644 gui/backend/jwt_middleware.go create mode 100755 gui/web/src/components/screens/LogAuth/LoginRedirect.js create mode 100755 gui/web/src/components/screens/LogAuth/LogoutButton.js create mode 100644 gui/web/src/kelp-ops-api/interceptor.js create mode 100644 support/guiconfig/config.go diff --git a/README.md b/README.md index 76db36a05..1472e9fc3 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ Your use of Kelp is governed by the Apache 2.0 open-source license. Please note * [Running Kelp](#running-kelp) * [Using CCXT](#using-ccxt) * [Using Postgres](#using-postgres) + * [Using Auth0](#using-auth0) * [Examples](#examples) * [Walkthrough Guides](#walkthrough-guides) * [Configuration Files](#configuration-files) @@ -184,6 +185,11 @@ You can find more details on the [CCXT_REST github page][ccxt-rest]. [Postgres][postgres] v12.1 or later must be installed for Kelp to automatically write trades to a sql database along with updating the trader config file. +### Using Auth0 + +A [auth0](https://auth0.com/) account is required. To use it, uncomment \[AUTH0] section in [Sample GUI config file](examples/configs/trader/sample_GUI_config.cfg) and enter your auth0 crendentials in required fields. +Note: AUTH0 is only applicable for Kelp GUI or Kaas Mode. Intructions of how to configure your auth0 account can be found [here](https://auth0.com/docs/quickstart/spa/react/01-login#configure-auth0) + ## Examples It's easier to learn with examples! Take a look at the walkthrough guides and sample configuration files below. @@ -208,6 +214,7 @@ The following reference config files are in the [examples folder](examples/confi - [Sample Balanced strategy config file](examples/configs/trader/sample_balanced.cfg) - [Sample Pendulum strategy config file](examples/configs/trader/sample_pendulum.cfg) - [Sample Mirror strategy config file](examples/configs/trader/sample_mirror.cfg) +- [Sample GUI(auth0 and other stuff) config file](examples/configs/trader/sample_GUI_config.cfg) ### Winning Educational Content from StellarBattle @@ -403,6 +410,7 @@ See the [Code of Conduct](CODE_OF_CONDUCT.md). [ccxt-rest]: https://github.com/franz-see/ccxt-rest [docker]: https://www.docker.com/ [postgres]: https://www.postgresql.org/ +[auth0]: https://auth0.com/ [kelp-battle-1]: https://stellarbattle.com/kelp-overview-battle/ [kelp-battle-1-winners]: https://medium.com/stellar-community/announcing-the-winners-of-the-first-kelpbot-stellarbattle-a6f28fef7776 [kraken]: https://www.kraken.com/ diff --git a/cmd/server_amd64.go b/cmd/server_amd64.go index cfcc4f7c5..adc625303 100644 --- a/cmd/server_amd64.go +++ b/cmd/server_amd64.go @@ -39,6 +39,8 @@ import ( "github.com/stellar/kelp/support/prefs" "github.com/stellar/kelp/support/sdk" "github.com/stellar/kelp/support/utils" + "github.com/stellar/kelp/support/guiconfig" + "github.com/stellar/go/support/config" ) const kelpAssetsPath = "/assets" @@ -72,6 +74,15 @@ type serverInputOptions struct { enableKaas *bool tlsCertFile *string tlsKeyFile *string + guiConfigPath *string +} + +// checks for required flag on CLI +func requiredFlags(flag string) { + e := serverCmd.MarkFlagRequired(flag) + if e != nil { + panic(e) + } } // String is the stringer method impl. @@ -80,6 +91,19 @@ func (o serverInputOptions) String() string { *o.port, *o.dev, *o.devAPIPort, *o.horizonTestnetURI, *o.horizonPubnetURI, *o.noHeaders, *o.verbose, *o.noElectron, *o.disablePubnet, *o.enableKaas) } +// function for reading custom config file and returning config struct with intilized value +func readGUIConfig(options serverInputOptions) guiconfig.GUIConfig { + var guiConfigInFunc guiconfig.GUIConfig + e := config.Read(*options.guiConfigPath, &guiConfigInFunc) + utils.CheckConfigError(guiConfigInFunc, e, *options.guiConfigPath) + if e != nil { + panic(fmt.Errorf("could not read GUI config file '%s': %s", *options.guiConfigPath, e)) + } + return guiConfigInFunc +} +// customConfigVar Variable with its equivalent struct #used to inject config values to jwt config var and to configure route +var auth0ConfigVar guiconfig.GUIConfig + func init() { options := serverInputOptions{} options.port = serverCmd.Flags().Uint16P("port", "p", 8000, "port on which to serve HTTP") @@ -95,6 +119,9 @@ func init() { options.enableKaas = serverCmd.Flags().Bool("enable-kaas", false, "enable kelp-as-a-service (KaaS) mode, which does not bring up browser or electron") options.tlsCertFile = serverCmd.Flags().String("tls-cert-file", "", "path to TLS certificate file") options.tlsKeyFile = serverCmd.Flags().String("tls-key-file", "", "path to TLS key file") + options.guiConfigPath = serverCmd.Flags().StringP("guiconfig", "c", "", "(required) gui-config for auth0 and other basic config file path") + + requiredFlags("guiconfig") serverCmd.Run = func(ccmd *cobra.Command, args []string) { isLocalMode := env == envDev @@ -143,6 +170,10 @@ func init() { log.Printf("initialized server with cli flag inputs: %s", options) + //calliing readGUIConfig func and then inject values into JWT_middleware customconfigvar + auth0ConfigVar = readGUIConfig(options) + backend.Auth0ConfigVarJWT = auth0ConfigVar + if runtime.GOOS == "windows" { if !*options.noElectron { log.Printf("input options had specified noElectron=false for windows, but electron is not supported on windows yet. force setting noElectron=true for windows.\n") @@ -382,6 +413,7 @@ func init() { *options.noHeaders, quit, metricsTracker, + auth0ConfigVar, ) if e != nil { panic(e) diff --git a/examples/configs/trader/sample_GUI_config.cfg b/examples/configs/trader/sample_GUI_config.cfg new file mode 100644 index 000000000..192cd3f94 --- /dev/null +++ b/examples/configs/trader/sample_GUI_config.cfg @@ -0,0 +1,11 @@ +# Sample UI config file for the kelp bot + +# uncomment the AUTH0 section below to enable +# [AUTH0] +# AUTH0_ENABLED=false +# #auth0 domain +# DOMAIN= #"domain_goes_here" #example "dev-*******.eu.auth0.com" +# #auth0 clientID +# CLIENT_ID= #"Client_id_goes_here" #examples "7I47ob2************XKF29hY5" +# #auth0 audience +# AUDIENCE= #"Audience/Identifier goes_here" \ No newline at end of file diff --git a/glide.lock b/glide.lock index 845089d7d..aa72ca769 100644 --- a/glide.lock +++ b/glide.lock @@ -23,6 +23,10 @@ imports: version: 155c2a10bbb1791fbafe89a9b64be05c64f16c81 - name: github.com/asticode/go-astilog version: 4318d9ca22883d107f0e27db25a91cddbe4a137e +- name: github.com/auth0/go-jwt-middleware + version: v1.0.0 +- name: github.com/form3tech-oss/jwt-go + version: 5b2d2b5f6c34ccb3b6b65f77f4706558067690ef - name: github.com/asticode/go-astitools version: d157a994ecbd55bca08e5daf4fe89518ddae4c50 subpackages: diff --git a/glide.yaml b/glide.yaml index 58c8b2a6b..868ec981f 100644 --- a/glide.yaml +++ b/glide.yaml @@ -17,6 +17,10 @@ import: - clients/horizonclient - support/config - support/errors +- package: github.com/auth0/go-jwt-middleware + version: v1.0.0 +- package: github.com/form3tech-oss/jwt-go + version: 5b2d2b5f6c34ccb3b6b65f77f4706558067690ef ############################################################## # manually added dependencies to fix glide import resolution ############################################################## diff --git a/gui/backend/api_server.go b/gui/backend/api_server.go index 2200e31a2..aa202b2cc 100644 --- a/gui/backend/api_server.go +++ b/gui/backend/api_server.go @@ -16,6 +16,7 @@ import ( "github.com/stellar/go/clients/horizonclient" "github.com/stellar/kelp/plugins" "github.com/stellar/kelp/support/kelpos" + "github.com/stellar/kelp/support/guiconfig" ) // UserData is the json data passed in to represent a user @@ -59,6 +60,7 @@ type APIServer struct { kelpErrorsByUserLock *sync.Mutex cachedOptionsMetadata metadata + guiConfig guiconfig.GUIConfig } // MakeAPIServer is a factory method @@ -76,6 +78,7 @@ func MakeAPIServer( noHeaders bool, quitFn func(), metricsTracker *plugins.MetricsTracker, + guiConfig guiconfig.GUIConfig, ) (*APIServer, error) { kelpBinPath := kos.GetBinDir().Join(filepath.Base(os.Args[0])) @@ -102,6 +105,7 @@ func MakeAPIServer( metricsTracker: metricsTracker, kelpErrorsByUser: map[string]kelpErrorDataForUser{}, kelpErrorsByUserLock: &sync.Mutex{}, + guiConfig: guiConfig, }, nil } diff --git a/gui/backend/jwt_middleware.go b/gui/backend/jwt_middleware.go new file mode 100644 index 000000000..17670e973 --- /dev/null +++ b/gui/backend/jwt_middleware.go @@ -0,0 +1,88 @@ +package backend + +import ( + "encoding/json" + "errors" + "net/http" + "fmt" + + jwtmiddleware "github.com/auth0/go-jwt-middleware" + "github.com/form3tech-oss/jwt-go" + "github.com/stellar/kelp/support/guiconfig" +) + +type Response struct { + Message string `json:"message"` +} + +type Jwks struct { + Keys []JSONWebKeys `json:"keys"` +} + +type JSONWebKeys struct { + Kty string `json:"kty"` + Kid string `json:"kid"` + Use string `json:"use"` + N string `json:"n"` + E string `json:"e"` + X5c []string `json:"x5c"` +} + +var Auth0ConfigVarJWT guiconfig.GUIConfig + +var JWTMiddlewareVar = jwtmiddleware.New(jwtmiddleware.Options{ + ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) { + // Verify 'iss' claim + iss := "https://" + Auth0ConfigVarJWT.Auth0Config.Domain + "/" + checkIss := token.Claims.(jwt.MapClaims).VerifyIssuer(iss, false) + if !checkIss { + return token, errors.New("Invalid issuer.") + } + + // Verify 'aud' claim + aud := Auth0ConfigVarJWT.Auth0Config.Audience + checkAud := token.Claims.(jwt.MapClaims).VerifyAudience(aud, false) + if !checkAud { + return token, errors.New("Invalid audience.") + } + + cert, err := getPemCert(token) + if err != nil { + return nil, fmt.Errorf("error when getting PEM certificate: %s", err) + } + + result, _ := jwt.ParseRSAPublicKeyFromPEM([]byte(cert)) + return result, nil + }, + SigningMethod: jwt.SigningMethodRS256, +}) + +func getPemCert(token *jwt.Token) (string, error) { + cert := "" + resp, err := http.Get("https://" + Auth0ConfigVarJWT.Auth0Config.Domain + "/.well-known/jwks.json") + + if err != nil { + return cert, err + } + defer resp.Body.Close() + + var jwks = Jwks{} + err = json.NewDecoder(resp.Body).Decode(&jwks) + + if err != nil { + return cert, err + } + + for k, _ := range jwks.Keys { + if token.Header["kid"] == jwks.Keys[k].Kid { + cert = "-----BEGIN CERTIFICATE-----\n" + jwks.Keys[k].X5c[0] + "\n-----END CERTIFICATE-----" + } + } + + if cert == "" { + err := errors.New("Unable to find appropriate key.") + return cert, err + } + + return cert, nil +} diff --git a/gui/backend/routes.go b/gui/backend/routes.go index 5fa0a555b..2230732a7 100644 --- a/gui/backend/routes.go +++ b/gui/backend/routes.go @@ -18,22 +18,27 @@ func SetRoutes(r *chi.Mux, s *APIServer) { r.Get("/serverMetadata", http.HandlerFunc(s.serverMetadata)) r.Get("/newSecretKey", http.HandlerFunc(s.newSecretKey)) r.Get("/optionsMetadata", http.HandlerFunc(s.optionsMetadata)) + var router chi.Router = r + if s.guiConfig.Auth0Config != nil && s.guiConfig.Auth0Config.Auth0Enabled { + // setting the router to use the JWT middleware to handle auth0 style JWT tokens + router = r.With(JWTMiddlewareVar.Handler) + } - r.Post("/listBots", http.HandlerFunc(s.listBots)) - r.Post("/genBotName", http.HandlerFunc(s.generateBotName)) - r.Post("/getNewBotConfig", http.HandlerFunc(s.getNewBotConfig)) - r.Post("/autogenerate", http.HandlerFunc(s.autogenerateBot)) - r.Post("/fetchKelpErrors", http.HandlerFunc(s.fetchKelpErrors)) - r.Post("/removeKelpErrors", http.HandlerFunc(s.removeKelpErrors)) - r.Post("/start", http.HandlerFunc(s.startBot)) - r.Post("/stop", http.HandlerFunc(s.stopBot)) - r.Post("/deleteBot", http.HandlerFunc(s.deleteBot)) - r.Post("/getState", http.HandlerFunc(s.getBotState)) - r.Post("/getBotInfo", http.HandlerFunc(s.getBotInfo)) - r.Post("/getBotConfig", http.HandlerFunc(s.getBotConfig)) - r.Post("/fetchPrice", http.HandlerFunc(s.fetchPrice)) - r.Post("/upsertBotConfig", http.HandlerFunc(s.upsertBotConfig)) - r.Post("/sendMetricEvent", http.HandlerFunc(s.sendMetricEvent)) + router.Post("/listBots", http.HandlerFunc(s.listBots)) + router.Post("/genBotName", http.HandlerFunc(s.generateBotName)) + router.Post("/getNewBotConfig", http.HandlerFunc(s.getNewBotConfig)) + router.Post("/autogenerate", http.HandlerFunc(s.autogenerateBot)) + router.Post("/fetchKelpErrors", http.HandlerFunc(s.fetchKelpErrors)) + router.Post("/removeKelpErrors", http.HandlerFunc(s.removeKelpErrors)) + router.Post("/start", http.HandlerFunc(s.startBot)) + router.Post("/stop", http.HandlerFunc(s.stopBot)) + router.Post("/deleteBot", http.HandlerFunc(s.deleteBot)) + router.Post("/getState", http.HandlerFunc(s.getBotState)) + router.Post("/getBotInfo", http.HandlerFunc(s.getBotInfo)) + router.Post("/getBotConfig", http.HandlerFunc(s.getBotConfig)) + router.Post("/fetchPrice", http.HandlerFunc(s.fetchPrice)) + router.Post("/upsertBotConfig", http.HandlerFunc(s.upsertBotConfig)) + router.Post("/sendMetricEvent", http.HandlerFunc(s.sendMetricEvent)) }) r.Get("/ping", http.HandlerFunc(s.ping)) -} +} \ No newline at end of file diff --git a/gui/backend/serverMetadata.go b/gui/backend/serverMetadata.go index a512acffa..a68034b41 100644 --- a/gui/backend/serverMetadata.go +++ b/gui/backend/serverMetadata.go @@ -4,18 +4,21 @@ import ( "encoding/json" "fmt" "net/http" + "github.com/stellar/kelp/support/guiconfig" ) // ServerMetadataResponse is the response from the /serverMetadata endpoint type ServerMetadataResponse struct { DisablePubnet bool `json:"disable_pubnet"` EnableKaas bool `json:"enable_kaas"` + GuiConfig guiconfig.GUIConfig `json:"guiconfig"` } func (s *APIServer) serverMetadata(w http.ResponseWriter, r *http.Request) { metadata := ServerMetadataResponse{ DisablePubnet: s.disablePubnet, EnableKaas: s.enableKaas, + GuiConfig: s.guiConfig, } b, e := json.Marshal(metadata) diff --git a/gui/web/package.json b/gui/web/package.json index b437ddabd..c35c3cc15 100644 --- a/gui/web/package.json +++ b/gui/web/package.json @@ -3,7 +3,9 @@ "version": "0.1.0", "private": true, "dependencies": { + "@auth0/auth0-react": "^1.5.0", "classnames": "^2.2.6", + "fetch-intercept": "^2.4.0", "node-sass": "^4.11.0", "react": "^16.8.4", "react-dom": "^16.8.4", diff --git a/gui/web/src/App.js b/gui/web/src/App.js index 1fc5b3be1..f570f83c4 100644 --- a/gui/web/src/App.js +++ b/gui/web/src/App.js @@ -12,6 +12,10 @@ import quit from './kelp-ops-api/quit'; import fetchKelpErrors from './kelp-ops-api/fetchKelpErrors'; import removeKelpErrors from './kelp-ops-api/removeKelpErrors'; import Welcome from './components/molecules/Welcome/Welcome'; +import LoginRedirect from './components/screens/LogAuth/LoginRedirect'; +import interceptor from './kelp-ops-api/interceptor'; +import LoadingAnimation from './components/atoms/LoadingAnimation/LoadingAnimation'; +import { Auth0Provider } from '@auth0/auth0-react'; let baseUrl = function () { let base_url = window.location.origin; @@ -325,10 +329,16 @@ class App extends Component { } render() { + if (this.state.server_metadata == null) { + return (); + } // construction of metricsTracker in server_amd64.go (isTestnet) needs to logically match this variable // we use the state because that is updated from the /serverMetadata endpoint const enablePubnetBots = this.state.server_metadata ? !this.state.server_metadata.disable_pubnet : false; + const config = this.state.server_metadata.guiconfig.auth0; + const auth0enabled = this.state.server_metadata.guiconfig.auth0 ? config.auth0_enabled : false; + let quitButton = ""; if (this.showQuitButton()) { quitButton = ( @@ -351,11 +361,12 @@ class App extends Component { const removeBotError = this.removeError.bind(this, Constants.ErrorType.bot); const findBotErrors = this.findErrors.bind(this, Constants.ErrorType.bot); - return ( + const innerAppComponent = (
{banner}
-
+ {auth0enabled ? () : ""} +
} /> @@ -372,7 +383,18 @@ class App extends Component {
); + + return (
+ {auth0enabled ? ( + {innerAppComponent} + ) : (innerAppComponent)} +
); } } -export default App; +export default App; \ No newline at end of file diff --git a/gui/web/src/components/molecules/Header/Header.js b/gui/web/src/components/molecules/Header/Header.js index 2f0b43ba6..9bdda8625 100644 --- a/gui/web/src/components/molecules/Header/Header.js +++ b/gui/web/src/components/molecules/Header/Header.js @@ -2,9 +2,11 @@ import React, { Component } from 'react'; import logo from '../../../assets/images/kelp-logo.svg'; import grid from '../../_styles/grid.module.scss'; import styles from './Header.module.scss'; +import LogoutButton from '../../screens/LogAuth/LogoutButton'; class Header extends Component { render() { + const auth0enabled = this.props.auth0enabled; return (
@@ -13,6 +15,7 @@ class Header extends Component { Kelp logo {this.props.version}
+ {auth0enabled ? () : ""}
diff --git a/gui/web/src/components/screens/LogAuth/LoginRedirect.js b/gui/web/src/components/screens/LogAuth/LoginRedirect.js new file mode 100755 index 000000000..eee2e6005 --- /dev/null +++ b/gui/web/src/components/screens/LogAuth/LoginRedirect.js @@ -0,0 +1,41 @@ +import React, { useEffect } from "react"; +import { useAuth0 } from "@auth0/auth0-react"; + +const LoginRedirect = () => { + const { user,isLoading,isAuthenticated, loginWithRedirect, getAccessTokenSilently } = useAuth0(); + + if (isLoading) { + return ""; + } + + if (!isAuthenticated) { + return loginWithRedirect(); + } + + if (isLoading) { + return ""; + } + + useEffect(() => { + + /* dont rename the user_id key because it is re-used in getUserData.js and both keys need to match*/ + + const userIDKey = 'user_id'; + const Auth0ID = JSON.stringify(user.sub).replace('"','').replace('|', '-').replace('"','') + localStorage.setItem(userIDKey, Auth0ID); + + try { + if(localStorage.getItem('accessToken') == null){ + getAccessTokenSilently().then(token => localStorage.setItem("accessToken", token)).then(window.location.reload()); + } + } catch (e) { + console.log(e.message); + } + }, []); + + return ( + <> + ); +}; + +export default LoginRedirect; \ No newline at end of file diff --git a/gui/web/src/components/screens/LogAuth/LogoutButton.js b/gui/web/src/components/screens/LogAuth/LogoutButton.js new file mode 100755 index 000000000..a2bddeaf3 --- /dev/null +++ b/gui/web/src/components/screens/LogAuth/LogoutButton.js @@ -0,0 +1,14 @@ +import React from "react"; +import { useAuth0 } from "@auth0/auth0-react"; + +const LogoutButton = () => { + const { logout } = useAuth0(); + + return ( + + ); +}; + +export default LogoutButton; \ No newline at end of file diff --git a/gui/web/src/kelp-ops-api/getUserData.js b/gui/web/src/kelp-ops-api/getUserData.js index b102f21f7..ae865ba33 100644 --- a/gui/web/src/kelp-ops-api/getUserData.js +++ b/gui/web/src/kelp-ops-api/getUserData.js @@ -1,6 +1,7 @@ import { v4 as uuidv4 } from "uuid"; export default () => { + /* dont rename the user_id key because it is re-used in LoginRedirect.js and both keys need to match*/ const userIDKey = 'user_id'; let userId = localStorage.getItem(userIDKey); diff --git a/gui/web/src/kelp-ops-api/interceptor.js b/gui/web/src/kelp-ops-api/interceptor.js new file mode 100644 index 000000000..000b6a26b --- /dev/null +++ b/gui/web/src/kelp-ops-api/interceptor.js @@ -0,0 +1,32 @@ +import fetchIntercept from 'fetch-intercept'; + +/* this file is not referenced anywhere but still being used because its registering interceptor on javascript fetch function globally */ + +let AccessToken = localStorage.getItem('accessToken'); + +export const interceptor = fetchIntercept.register({ + request: function (url, config) { + // Modify the url or config here + const withDefaults = Object.assign({}, config); + if (!config || !config.headers) { + withDefaults.headers = new Headers({}) + }; + withDefaults.headers.append('AUTHORIZATION', `Bearer ${AccessToken}`) + return [url, withDefaults] + }, + + requestError: function (error) { + // Called when an error occured during another 'request' interceptor call + return Promise.reject(error); + }, + + response: function (response) { + // Modify the reponse object + return response; + }, + + responseError: function (error) { + // Handle an fetch error + return Promise.reject(error); + } +}); diff --git a/gui/web/src/kelp-ops-api/serverMetadata.js b/gui/web/src/kelp-ops-api/serverMetadata.js index 154e65627..2b6e68671 100644 --- a/gui/web/src/kelp-ops-api/serverMetadata.js +++ b/gui/web/src/kelp-ops-api/serverMetadata.js @@ -2,4 +2,4 @@ export default (baseUrl) => { return fetch(baseUrl + "/api/v1/serverMetadata").then(resp => { return resp.json(); }); -}; \ No newline at end of file +}; diff --git a/gui/web/src/kelp-ops-api/version.js b/gui/web/src/kelp-ops-api/version.js index 94e6b7a3f..ed380626a 100644 --- a/gui/web/src/kelp-ops-api/version.js +++ b/gui/web/src/kelp-ops-api/version.js @@ -2,4 +2,4 @@ export default (baseUrl) => { return fetch(baseUrl + "/api/v1/version").then(resp => { return resp.text(); }); -}; \ No newline at end of file +}; diff --git a/gui/web/yarn.lock b/gui/web/yarn.lock index d39d519e0..bcec36ccf 100644 --- a/gui/web/yarn.lock +++ b/gui/web/yarn.lock @@ -2,6 +2,26 @@ # yarn lockfile v1 +"@auth0/auth0-react@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@auth0/auth0-react/-/auth0-react-1.5.0.tgz#f2dcfef1cab59f8555ade4e2b5ec13befce67395" + integrity sha512-LFJUd3V6aKCKxblJvrz3JIHzyBCz2X3ax5RdSjxZwGr5XxPwdhogeBWcyijnuB8moKD2ncl56OpOslbVGLIJ/w== + dependencies: + "@auth0/auth0-spa-js" "^1.15.0" + +"@auth0/auth0-spa-js@^1.15.0": + version "1.15.0" + resolved "https://registry.yarnpkg.com/@auth0/auth0-spa-js/-/auth0-spa-js-1.15.0.tgz#9fa563b7b2e49dc4c6a465a0c240078322e80159" + integrity sha512-d/crchAbhncl9irIMuw1zNSZgX+id0U7mzASQr2htMJ73JCYaAvBSdGXL0WcYS4yBm1Xsx1JYm3b5tEZ5p/ncg== + dependencies: + abortcontroller-polyfill "^1.7.1" + browser-tabs-lock "^1.2.13" + core-js "^3.11.0" + es-cookie "^1.3.2" + fast-text-encoding "^1.0.3" + promise-polyfill "^8.2.0" + unfetch "^4.2.0" + "@babel/code-frame@7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0.tgz#06e2ab19bdb535385559aabb5ba59729482800f8" @@ -1327,6 +1347,11 @@ abbrev@1: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== +abortcontroller-polyfill@^1.7.1: + version "1.7.3" + resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz#1b5b487bd6436b5b764fd52a612509702c3144b5" + integrity sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q== + accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" @@ -2136,6 +2161,13 @@ browser-resolve@^1.11.3: dependencies: resolve "1.1.7" +browser-tabs-lock@^1.2.13: + version "1.2.14" + resolved "https://registry.yarnpkg.com/browser-tabs-lock/-/browser-tabs-lock-1.2.14.tgz#f4ba30810d20199a1858c102da1f91e339250728" + integrity sha512-ssSpCRcvFe4vc098LDnrJOQDfZiG35KhQGB9hthTbwJk5mmUkePwhcMlW61NH3YuIE2Y9uGLqf9yxEBKbaDlaw== + dependencies: + lodash ">=4.17.21" + browserify-aes@^1.0.0, browserify-aes@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" @@ -2798,6 +2830,11 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.3: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c" integrity sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg== +core-js@^3.11.0: + version "3.14.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.14.0.tgz#62322b98c71cc2018b027971a69419e2425c2a6c" + integrity sha512-3s+ed8er9ahK+zJpp9ZtuVcDoFzHNiZsPbNAAE4KXgrRHbjSqqNN6xGSXq6bq7TZIbKj4NLrLb6bJ5i+vSVjHA== + core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -3580,6 +3617,11 @@ es-abstract@^1.17.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2: string.prototype.trimleft "^2.1.1" string.prototype.trimright "^2.1.1" +es-cookie@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/es-cookie/-/es-cookie-1.3.2.tgz#80e831597f72a25721701bdcb21d990319acd831" + integrity sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q== + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -4039,6 +4081,11 @@ fast-levenshtein@~2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= +fast-text-encoding@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53" + integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig== + fastparse@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" @@ -4065,6 +4112,11 @@ fb-watchman@^2.0.0: dependencies: bser "2.1.1" +fetch-intercept@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/fetch-intercept/-/fetch-intercept-2.4.0.tgz#be8b0f17abaadeea6de52ecdb6e9ed081f03480a" + integrity sha512-BPZ2LM9Dh1ua2ovQf03N6rhWg1qxdVD5qK/G4llvcemt6M+jjxCuIDxJ+6IiG+uz//3UQmgfKEv0gOGvYIxZ7g== + figgy-pudding@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" @@ -6294,6 +6346,11 @@ lodash.uniq@^4.5.0: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +lodash@>=4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + loglevel@^1.4.1: version "1.6.7" resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.7.tgz#b3e034233188c68b889f5b862415306f565e2c56" @@ -8223,6 +8280,11 @@ promise-inflight@^1.0.1: resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= +promise-polyfill@^8.2.0: + version "8.2.0" + resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.2.0.tgz#367394726da7561457aba2133c9ceefbd6267da0" + integrity sha512-k/TC0mIcPVF6yHhUvwAp7cvL6I2fFV7TzF1DuGPI8mBh4QQazf36xCKEHKTZKRysEoTQoQdKyP25J8MPJp7j5g== + promise@8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/promise/-/promise-8.0.2.tgz#9dcd0672192c589477d56891271bdc27547ae9f0" @@ -10113,6 +10175,11 @@ uglify-js@^3.1.4: commander "~2.20.3" source-map "~0.6.1" +unfetch@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" + integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== + unicode-canonical-property-names-ecmascript@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" diff --git a/support/guiconfig/config.go b/support/guiconfig/config.go new file mode 100644 index 000000000..8f9ef6627 --- /dev/null +++ b/support/guiconfig/config.go @@ -0,0 +1,24 @@ +package guiconfig + +import ( + "github.com/stellar/kelp/support/utils" +) + +type Auth0Config struct { + Auth0Enabled bool `valid:"-" toml:"AUTH0_ENABLED" json:"auth0_enabled"` + Domain string `valid:"-" toml:"DOMAIN"json:"domain"` + ClientId string `valid:"-" toml:"CLIENT_ID"json:"client_id"` + Audience string `valid:"-" toml:"AUDIENCE"json:"audience"` +} + +type GUIConfig struct { + Auth0Config *Auth0Config `valid:"-" toml:"AUTH0" json:"auth0"` +} + +// String impl. +func (g GUIConfig) String() string { + return utils.StructString(g, 0, map[string]func(interface{}) interface{}{ + "CLIENT_ID": utils.Hide, + "DOMAIN": utils.Hide, + }) +} \ No newline at end of file