From e80281c941173d9a5a4b0eced073d31ab312c9de Mon Sep 17 00:00:00 2001 From: Pierre-Olivier Mercier Date: Sun, 3 Jan 2021 22:40:47 +0100 Subject: [PATCH] Add NO_AUTH configuration option. This option permit to use happyDNS without creating accounts. --- api/handlers.go | 97 ++++++++++++---------- api/user_auth.go | 16 +++- api/users.go | 2 +- config/cli.go | 1 + config/config.go | 3 + htdocs/src/App.vue | 9 ++- htdocs/src/locales/en.json | 2 + htdocs/src/views/me.vue | 159 +++++++++++++++++++------------------ main.go | 18 +++++ model/user.go | 8 +- 10 files changed, 190 insertions(+), 125 deletions(-) diff --git a/api/handlers.go b/api/handlers.go index 586a119..bd97b94 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -211,7 +211,7 @@ type RequestResources struct { Zone *happydns.Zone } -func apiAuthHandler(f func(*config.Options, *RequestResources, io.Reader) Response) func(http.ResponseWriter, *http.Request, httprouter.Params) { +func apiOptionalAuthHandler(noauthcb func(*config.Options, *RequestResources, io.Reader) Response, authcb func(*config.Options, *RequestResources, io.Reader) Response) func(http.ResponseWriter, *http.Request, httprouter.Params) { return func(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { if addr := r.Header.Get("X-Forwarded-For"); addr != "" { r.RemoteAddr = addr @@ -253,48 +253,61 @@ func apiAuthHandler(f func(*config.Options, *RequestResources, io.Reader) Respon } } - if sessionid == nil || len(sessionid) == 0 { - logResponse(r, APIErrorResponse{ - err: fmt.Errorf("Authorization required"), - status: http.StatusUnauthorized, - }.WriteResponse(w)) - } else if session, err := storage.MainStore.GetSession(sessionid); err != nil { - logResponse(r, APIErrorResponse{ - err: err, - status: http.StatusUnauthorized, - cookies: []*http.Cookie{&http.Cookie{ - Name: "happydns_session", - Value: "", - Path: opts.BaseURL + "/", - Expires: time.Unix(0, 0), - Secure: opts.DevProxy == "", - HttpOnly: true, - }}, - }.WriteResponse(w)) - } else if user, err := storage.MainStore.GetUser(session.IdUser); err != nil { - logResponse(r, APIErrorResponse{ - err: err, - status: http.StatusUnauthorized, - cookies: []*http.Cookie{&http.Cookie{ - Name: "happydns_session", - Value: "", - Path: opts.BaseURL + "/", - Expires: time.Unix(0, 0), - Secure: opts.DevProxy == "", - HttpOnly: true, - }}, - }.WriteResponse(w)) - } else { - req := &RequestResources{ - Ps: ps, - Session: session, - User: user, - } - logResponse(r, f(opts, req, r.Body).WriteResponse(w)) + var err error + req := &RequestResources{ + Ps: ps, + } - if session.HasChanged() { - storage.MainStore.UpdateSession(session) - } + if sessionid == nil || len(sessionid) == 0 { + logResponse(r, noauthcb(opts, req, r.Body).WriteResponse(w)) + } else if req.Session, err = storage.MainStore.GetSession(sessionid); err != nil { + logResponse(r, APIErrorResponse{ + err: err, + status: http.StatusUnauthorized, + cookies: []*http.Cookie{&http.Cookie{ + Name: "happydns_session", + Value: "", + Path: opts.BaseURL + "/", + Expires: time.Unix(0, 0), + Secure: opts.DevProxy == "", + HttpOnly: true, + }}, + }.WriteResponse(w)) + } else if req.User, err = storage.MainStore.GetUser(req.Session.IdUser); err != nil { + logResponse(r, APIErrorResponse{ + err: err, + status: http.StatusUnauthorized, + cookies: []*http.Cookie{&http.Cookie{ + Name: "happydns_session", + Value: "", + Path: opts.BaseURL + "/", + Expires: time.Unix(0, 0), + Secure: opts.DevProxy == "", + HttpOnly: true, + }}, + }.WriteResponse(w)) + } else if req.User.Email == NO_AUTH_ACCOUNT && !opts.NoAuth { + logResponse(r, noauthcb(opts, req, r.Body).WriteResponse(w)) + } else { + logResponse(r, authcb(opts, req, r.Body).WriteResponse(w)) } } + +} + +func apiAuthHandler(f func(*config.Options, *RequestResources, io.Reader) Response) func(http.ResponseWriter, *http.Request, httprouter.Params) { + return apiOptionalAuthHandler(func(opts *config.Options, req *RequestResources, _ io.Reader) Response { + return APIErrorResponse{ + err: fmt.Errorf("Authorization required"), + status: http.StatusUnauthorized, + } + }, func(opts *config.Options, req *RequestResources, r io.Reader) Response { + response := f(opts, req, r) + + if req.Session.HasChanged() { + storage.MainStore.UpdateSession(req.Session) + } + + return response + }) } diff --git a/api/user_auth.go b/api/user_auth.go index 562ebb1..06f97e5 100644 --- a/api/user_auth.go +++ b/api/user_auth.go @@ -35,6 +35,7 @@ import ( "encoding/base64" "encoding/json" "errors" + "fmt" "io" "log" "net/http" @@ -47,10 +48,12 @@ import ( "git.happydns.org/happydns/storage" ) +const NO_AUTH_ACCOUNT = "_no_auth" + var AuthFunc = checkAuth func init() { - router.GET("/api/auth", apiAuthHandler(displayAuthToken)) + router.GET("/api/auth", apiOptionalAuthHandler(displayNotAuthToken, displayAuthToken)) router.POST("/api/auth", ApiHandler(func(opts *config.Options, ps httprouter.Params, b io.Reader) Response { return AuthFunc(opts, ps, b) })) @@ -73,6 +76,17 @@ func currentUser(u *happydns.User) *DisplayUser { } } +func displayNotAuthToken(opts *config.Options, req *RequestResources, _ io.Reader) Response { + if opts.NoAuth { + return completeAuth(opts, NO_AUTH_ACCOUNT, NO_AUTH_ACCOUNT) + } else { + return APIErrorResponse{ + err: fmt.Errorf("Authorization required"), + status: http.StatusUnauthorized, + } + } +} + func displayAuthToken(_ *config.Options, req *RequestResources, _ io.Reader) Response { return APIResponse{ response: currentUser(req.User), diff --git a/api/users.go b/api/users.go index 138e035..322c5d5 100644 --- a/api/users.go +++ b/api/users.go @@ -151,7 +151,7 @@ func registerUser(opts *config.Options, p httprouter.Params, body io.Reader) Res if len(uu.Password) <= 7 { return APIErrorResponse{ - err: errors.New("The given email is invalid."), + err: errors.New("The given password is invalid."), } } diff --git a/config/cli.go b/config/cli.go index 7e5c8bc..0d55cf8 100644 --- a/config/cli.go +++ b/config/cli.go @@ -47,6 +47,7 @@ func (o *Options) declareFlags() { flag.StringVar(&o.BaseURL, "baseurl", o.BaseURL, "URL prepended to each URL") flag.StringVar(&o.DefaultNameServer, "default-ns", o.DefaultNameServer, "Adress to the default name server") flag.Var(&o.StorageEngine, "storage-engine", fmt.Sprintf("Select the storage engine between %v", storage.GetStorageEngines())) + flag.BoolVar(&o.NoAuth, "no-auth", false, "Disable user access control, use default account") // Others flags are declared in some other files likes sources, storages, ... when they need specials configurations } diff --git a/config/config.go b/config/config.go index 87e7859..ca4f59b 100644 --- a/config/config.go +++ b/config/config.go @@ -66,6 +66,9 @@ type Options struct { // StorageEngine points to the storage engine used. StorageEngine storage.StorageEngine + + // NoAuth controls if there is user access control or not. + NoAuth bool } // BuildURL appends the given url to the absolute ExternalURL. diff --git a/htdocs/src/App.vue b/htdocs/src/App.vue index a63047c..cde0631 100644 --- a/htdocs/src/App.vue +++ b/htdocs/src/App.vue @@ -51,9 +51,12 @@ {{ $t('menu.my-domains') }} @@ -69,8 +72,8 @@ {{ $t('menu.my-account') }} - - + + {{ $t('menu.logout') }} diff --git a/htdocs/src/locales/en.json b/htdocs/src/locales/en.json index b7af67c..c892c95 100644 --- a/htdocs/src/locales/en.json +++ b/htdocs/src/locales/en.json @@ -140,6 +140,7 @@ "content": "The page you are look for was not found." }, "account-delete": "An error occurs when trying to delete your account", + "account-no-auth": "You're using happyDNS without authentication. You cannot manage other account properties.", "address": "Email address is required", "address-valid": "A valid email address is required", "domain-access": "An error occurs when trying to access domain's list.", @@ -173,6 +174,7 @@ "dns-resolver": "DNS resolver", "my-account": "My account", "logout": "Logout", + "quick-menu": "Quick Access", "signup": "Sign up", "signin": "Sign in" }, diff --git a/htdocs/src/views/me.vue b/htdocs/src/views/me.vue index c96284d..1da64f6 100644 --- a/htdocs/src/views/me.vue +++ b/htdocs/src/views/me.vue @@ -63,85 +63,90 @@ -

- {{ $t('password.change') }} -

- - - - - - - - +

+ {{ $t('password.change') }} +

+ + + + + + + - - - + + + - -
- - {{ $t('password.change') }} - -
-
-
-
-
-

- {{ $t('account.delete.delete') }} -

- - -

- {{ $t('account.delete.confirm') }} -

- - {{ $t('account.delete.delete') }} - -

- - {{ $t('account.delete.consequence') }} - -

-
-
+ :label="$t('password.confirm-new')" + label-for="passwordconfirm-input" + :invalid-feedback="$t('errors.password-match')" + > + +
+
+ + {{ $t('password.change') }} + +
+
+
+
+
+

+ {{ $t('account.delete.delete') }} +

+ + +

+ {{ $t('account.delete.confirm') }} +

+ + {{ $t('account.delete.delete') }} + +

+ + {{ $t('account.delete.consequence') }} + +

+
+
+ +
+ {{ $t('errors.account-no-auth') }} +

{{ $t('account.delete.confirm-twice') }} diff --git a/main.go b/main.go index 73a9097..14c19ef 100644 --- a/main.go +++ b/main.go @@ -48,6 +48,7 @@ import ( "git.happydns.org/happydns/admin" "git.happydns.org/happydns/api" "git.happydns.org/happydns/config" + "git.happydns.org/happydns/model" "git.happydns.org/happydns/storage" _ "git.happydns.org/happydns/sources/alwaysdata" @@ -126,6 +127,23 @@ func main() { } } + if opts.NoAuth { + // Check if the default account exists. + if !storage.MainStore.UserExists(api.NO_AUTH_ACCOUNT) { + if user, err := happydns.NewUser(api.NO_AUTH_ACCOUNT, ""); err != nil { + log.Fatal("Unable to create default account:", err) + } else { + user.Settings = *happydns.DefaultUserSettings() + if err := storage.MainStore.CreateUser(user); err != nil { + log.Fatal("Unable to create default account in database:", err) + } else { + log.Println("Default account for NoAuth created.") + } + } + } + log.Println("WARNING: NoAuth option has to be use for testing or personnal purpose behind another restriction/authentication method.") + } + log.Println("Do database migrations...") if err = storage.MainStore.DoMigration(); err != nil { log.Fatal("Cannot migrate database: ", err) diff --git a/model/user.go b/model/user.go index 42aef13..eb39a51 100644 --- a/model/user.go +++ b/model/user.go @@ -80,7 +80,9 @@ func NewUser(email string, password string) (u *User, err error) { RegistrationTime: &t, } - err = u.DefinePassword(password) + if len(password) != 0 { + err = u.DefinePassword(password) + } return } @@ -118,6 +120,10 @@ func (u *User) DefinePassword(password string) (err error) { // CheckAuth compares the given password to the hashed one in the User struct. func (u *User) CheckAuth(password string) bool { + if len(password) < 8 { + return false + } + return bcrypt.CompareHashAndPassword(u.Password, []byte(password)) == nil }