Skip to content

Commit

Permalink
Auth: disable via cli flag (#17249)
Browse files Browse the repository at this point in the history
  • Loading branch information
naltatis authored Nov 14, 2024
1 parent e71f760 commit 576cd71
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 14 deletions.
3 changes: 3 additions & 0 deletions cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ const (
flagIgnoreDatabase = "ignore-db"
flagIgnoreDatabaseDescription = "Run command ignoring service database"

flagDisableAuth = "disable-auth"
flagDisableAuthDescription = "Disable authentication (dangerous)"

flagBatteryMode = "battery-mode"
flagBatteryModeDescription = "Set battery mode (normal, hold, charge)"
flagBatteryModeWait = "battery-mode-wait"
Expand Down
11 changes: 10 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/evcc-io/evcc/server"
"github.com/evcc-io/evcc/server/updater"
"github.com/evcc-io/evcc/util"
"github.com/evcc-io/evcc/util/auth"
"github.com/evcc-io/evcc/util/config"
"github.com/evcc-io/evcc/util/pipe"
"github.com/evcc-io/evcc/util/sponsor"
Expand Down Expand Up @@ -75,6 +76,8 @@ func init() {

rootCmd.Flags().Bool("profile", false, "Expose pprof profiles")
bind(rootCmd, "profile")

rootCmd.Flags().Bool(flagDisableAuth, false, flagDisableAuthDescription)
}

// initConfig reads in config file and ENV variables if set
Expand Down Expand Up @@ -270,7 +273,13 @@ func runRoot(cmd *cobra.Command, args []string) {
// allow web access for vehicles
configureAuth(conf.Network, config.Instances(config.Vehicles().Devices()), httpd.Router(), valueChan)

httpd.RegisterSystemHandler(valueChan, cache, func() {
auth := auth.New()
if ok, _ := cmd.Flags().GetBool(flagDisableAuth); ok {
log.WARN.Println("❗❗❗ Authentication is disabled. This is dangerous. Your data and credentials are not protected.")
auth.Disable()
}

httpd.RegisterSystemHandler(valueChan, cache, auth, func() {
log.INFO.Println("evcc was stopped by user. OS should restart the service. Or restart manually.")
once.Do(func() { close(stopC) }) // signal loop to end
})
Expand Down
3 changes: 1 addition & 2 deletions server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,8 @@ func (s *HTTPd) RegisterSiteHandlers(site site.API, valueChan chan<- util.Param)
}

// RegisterSystemHandler provides system level handlers
func (s *HTTPd) RegisterSystemHandler(valueChan chan<- util.Param, cache *util.Cache, shutdown func()) {
func (s *HTTPd) RegisterSystemHandler(valueChan chan<- util.Param, cache *util.Cache, auth auth.Auth, shutdown func()) {
router := s.Server.Handler.(*mux.Router)
auth := auth.New()

// api
api := router.PathPrefix("/api").Subrouter()
Expand Down
10 changes: 10 additions & 0 deletions server/http_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ func jwtFromRequest(r *http.Request) string {
// authStatusHandler login status (true/false) based on jwt token. Error if admin password is not configured
func authStatusHandler(auth auth.Auth) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if auth.Disabled() {
w.Write([]byte("true"))
return
}

if !auth.IsAdminPasswordConfigured() {
http.Error(w, "Not implemented", http.StatusNotImplemented)
return
Expand Down Expand Up @@ -130,6 +135,11 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) {
func ensureAuthHandler(auth auth.Auth) mux.MiddlewareFunc {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if auth.Disabled() {
next.ServeHTTP(w, r)
return
}

// check jwt token
ok, err := auth.ValidateJwtToken(jwtFromRequest(r))
if !ok || err != nil {
Expand Down
18 changes: 17 additions & 1 deletion tests/auth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ test("http iframe hint", async ({ page }) => {
});

test("update password", async ({ page }) => {
const instance = await start(BASIC, "password.sql");
await start(BASIC, "password.sql");
await page.goto("/");

const oldPassword = "secret";
Expand Down Expand Up @@ -140,3 +140,19 @@ test("update password", async ({ page }) => {

await stop();
});

test("disable auth", async ({ page }) => {
await start(BASIC, null, "--disable-auth");
await page.goto("/");

// no password modal
const modal = page.getByTestId("password-modal");
await expect(modal).not.toBeVisible();

// configuration page without login
await page.getByTestId("topnavigation-button").click();
await page.getByRole("link", { name: "Configuration" }).click();
await expect(page.getByRole("heading", { name: "Configuration" })).toBeVisible();

await stop();
});
24 changes: 16 additions & 8 deletions tests/evcc.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ function dbPath() {
return path.join(os.tmpdir(), file);
}

export async function start(config, sqlDumps) {
export async function start(config, sqlDumps, flags) {
await _clean();
if (sqlDumps) {
await _restoreDatabase(sqlDumps);
}
return await _start(config);
return await _start(config, flags);
}

export async function stop(instance) {
Expand Down Expand Up @@ -77,14 +77,15 @@ async function _restoreDatabase(sqlDumps) {
}
}

async function _start(config) {
async function _start(config, flags = []) {
const configFile = config.includes("/") ? config : `tests/${config}`;
const port = workerPort();
log(`wait until port ${port} is available`);
// wait for port to be available
await waitOn({ resources: [`tcp:${port}`], reverse: true, log: true });
log("starting evcc", { config, port });
const instance = spawn(BINARY, ["--config", configFile], {
const additionalFlags = typeof flags === "string" ? [flags] : flags;
log("starting evcc", { config, port, additionalFlags });
const instance = spawn(BINARY, ["--config", configFile, additionalFlags], {
env: { EVCC_NETWORK_PORT: port.toString(), EVCC_DATABASE_DSN: dbPath() },
stdio: ["pipe", "pipe", "pipe"],
});
Expand All @@ -108,10 +109,17 @@ async function _stop(instance) {
log("evcc is down", { port });
return;
}
// check if auth is required
const res = await axios.get(`${baseUrl()}/api/auth/status`);
log("auth status", res.status, res.statusText, res.data);
let cookie;
// login required
if (!res.data) {
const res = await axios.post(`${baseUrl()}/api/auth/login`, { password: "secret" });
log("login", res.status, res.statusText);
cookie = res.headers["set-cookie"];
}
log("shutting down evcc", { port });
const res = await axios.post(`${baseUrl()}/api/auth/login`, { password: "secret" });
log(res.status, res.statusText);
const cookie = res.headers["set-cookie"];
await axios.post(`${baseUrl()}/api/system/shutdown`, {}, { headers: { cookie } });
log(`wait until port ${port} is closed`);
await waitOn({ resources: [`tcp:${port}`], reverse: true, log: true });
Expand Down
15 changes: 13 additions & 2 deletions util/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,21 @@ type Auth interface {
GenerateJwtToken(time.Duration) (string, error)
ValidateJwtToken(string) (bool, error)
IsAdminPasswordConfigured() bool
Disable()
Disabled() bool
}

type auth struct {
settings settings.API
disabled bool
}

func New() Auth {
return &auth{settings: new(settings.Settings)}
return &auth{settings: new(settings.Settings), disabled: false}
}

func NewMock(settings settings.API) Auth {
return &auth{settings: settings}
return &auth{settings: settings, disabled: false}
}

func (a *auth) hashPassword(password string) (string, error) {
Expand Down Expand Up @@ -140,3 +143,11 @@ func (a *auth) ValidateJwtToken(tokenString string) (bool, error) {

return true, nil
}

func (a *auth) Disable() {
a.disabled = true
}

func (a *auth) Disabled() bool {
return a.disabled
}

0 comments on commit 576cd71

Please sign in to comment.