From dfc7d9b10cc61c59bc3ce5cd5594a2250952223f Mon Sep 17 00:00:00 2001 From: Liam Burnand Date: Wed, 10 Jan 2024 13:40:08 +0000 Subject: [PATCH 1/3] Adding some new functions but nothing finalised by any means --- main.go | 20 ++++++++++++ router.go | 1 + utils/hash.go | 9 +++++ views/api.go | 48 ++++++++++++++++++++------- views/login.go | 89 +++++++++++++++++++++++++++++++++++++++++++++++--- views/views.go | 14 ++++++++ 6 files changed, 166 insertions(+), 15 deletions(-) diff --git a/main.go b/main.go index b9deb84..0d1dbd9 100644 --- a/main.go +++ b/main.go @@ -74,6 +74,16 @@ func main() { domainName := os.Getenv("WAUTH_DOMAIN_NAME") + adPort, err := strconv.Atoi(os.Getenv("WAUTH_AD_PORT")) + if err != nil { + log.Fatalf("failed to get ad port env: %+v", err) + } + + adSecurity, err := strconv.Atoi(os.Getenv("WAUTH_AD_SECURITY")) + if err != nil { + log.Fatalf("failed to get ad security env: %+v", err) + } + // Generate config conf := &views.Config{ Version: Version, @@ -96,6 +106,16 @@ func main() { AuthenticationKey: os.Getenv("WAUTH_AUTHENTICATION_KEY"), SigningKey: signingKey, }, + AD: views.ADConfig{ + Server: os.Getenv("WAUTH_AD_SERVER"), + Port: adPort, + BaseDN: os.Getenv("WAUTH_AD_BASE_DN"), + Security: adSecurity, + Bind: views.ADBind{ + Username: os.Getenv("WAUTH_AD_BIND_USERNAME"), + Password: os.Getenv("WAUTH_AD_BIND_PASSWORD"), + }, + }, } v := views.New(conf, dbHost) diff --git a/router.go b/router.go index a2de7c2..0dc99d3 100644 --- a/router.go +++ b/router.go @@ -64,6 +64,7 @@ func (r *Router) middleware() { "https://internal." + r.config.BaseDomainName, "https://docs." + r.config.BaseDomainName, "https://history." + r.config.BaseDomainName, + "http://localhost:*", }, AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept, echo.HeaderAccessControlAllowCredentials, echo.HeaderAccessControlAllowOrigin, echo.HeaderAuthorization}, AllowMethods: []string{http.MethodGet, http.MethodPost}, diff --git a/utils/hash.go b/utils/hash.go index 0d489db..9ad1fda 100644 --- a/utils/hash.go +++ b/utils/hash.go @@ -7,6 +7,7 @@ import ( "math/big" whirl "github.com/balacode/zr-whirl" + "golang.org/x/crypto/scrypt" ) type Type int @@ -34,6 +35,14 @@ func HashPass(password string) string { return next } +func HashPassScrypt(password, salt []byte) (string, error) { + hash, err := scrypt.Key(password, salt, 32768, 16, 2, 64) + if err != nil { + return "", fmt.Errorf("failed to generate hash: %w", err) + } + return hex.EncodeToString(hash), nil +} + // GenerateRandom generates a random string for either password or salt func GenerateRandom(t Type) (string, error) { switch t { diff --git a/views/api.go b/views/api.go index 384d853..39f5da6 100644 --- a/views/api.go +++ b/views/api.go @@ -6,6 +6,7 @@ import ( "fmt" "log" "net/http" + "net/url" "strings" "time" @@ -206,17 +207,42 @@ func (v *Views) SetTokenHandler(c echo.Context) error { return c.JSON(http.StatusInternalServerError, data) } - c.Response().Header().Set("Content-Type", "application/json") - c.Response().WriteHeader(http.StatusCreated) - _, err = c.Response().Write(tokenByte) - if err != nil { - log.Printf("failed to write token to http body: %+v", err) - data := struct { - Error error `json:"error"` - }{ - Error: fmt.Errorf("failed to write token to http body: %w", err), - } - return c.JSON(http.StatusInternalServerError, data) + _ = tokenByte + + callback := "" + callbackURL, err := url.Parse(c.QueryParam("callback")) + fmt.Println(callbackURL.String(), err) + if err == nil /*&& strings.HasSuffix(callbackURL.Host, v.conf.BaseDomainName)*/ && callbackURL.String() != "" { + callback = callbackURL.String() + } + //c.Response().Header().Set("Content-Type", "application/json") + c.Response().Header().Set("Authorization", "Bearer "+tokenString) + cookie := new(http.Cookie) + cookie.Name = "token" + cookie.Expires = time.Now().Add(30 * time.Second) + cookie.Value = tokenString + cookie.Secure = false + cookie.HttpOnly = false + cookie.Domain = "localhost" + c.SetCookie(cookie) + http.SetCookie(c.Response().Writer, cookie) + c.Response().Committed = false + //c.Response().Write(tokenByte) + //_, err = c.Response().Write(tokenByte) + //if err != nil { + // log.Printf("failed to write token to http body: %+v", err) + // data := struct { + // Error error `json:"error"` + // }{ + // Error: fmt.Errorf("failed to write token to http body: %w", err), + // } + // return c.JSON(http.StatusInternalServerError, data) + //} + if len(callback) > 0 { + //c.Response().Header().Set("Location", callback) + //c.Response().WriteHeader(http.StatusFound) + return c.Redirect(http.StatusFound, callback+"?token="+tokenString) + //c.Redirect() } return nil } diff --git a/views/login.go b/views/login.go index 06aa634..692ecc0 100644 --- a/views/login.go +++ b/views/login.go @@ -5,14 +5,14 @@ import ( "log" "net/http" "net/url" - "strings" "github.com/google/uuid" "github.com/labstack/echo/v4" + emailParser "github.com/mcnijman/go-emailaddress" "github.com/patrickmn/go-cache" - "github.com/ystv/web-auth/templates" "gopkg.in/guregu/null.v4" + "github.com/ystv/web-auth/templates" "github.com/ystv/web-auth/user" ) @@ -30,7 +30,7 @@ func (v *Views) LoginFunc(c echo.Context) error { // Check if there is a callback request callbackURL, err := url.Parse(c.QueryParam("callback")) - if err == nil && strings.HasSuffix(callbackURL.Host, v.conf.BaseDomainName) && callbackURL.String() != "" { + if err == nil && /*strings.HasSuffix(callbackURL.Host, v.conf.BaseDomainName) &&*/ callbackURL.String() != "" { context.Callback = callbackURL.String() } // Check if authenticated @@ -54,12 +54,17 @@ func (v *Views) LoginFunc(c echo.Context) error { callback := "/internal" callbackURL, err := url.Parse(c.QueryParam("callback")) - if err == nil && strings.HasSuffix(callbackURL.Host, v.conf.BaseDomainName) && callbackURL.String() != "" { + if err == nil /*&& strings.HasSuffix(callbackURL.Host, v.conf.BaseDomainName)*/ && callbackURL.String() != "" { callback = callbackURL.String() } // Authentication u, resetPw, err := v.user.VerifyUser(c.Request().Context(), u) if err != nil { + address, _ := emailParser.Parse(username) + if address != nil { + u.LDAPUsername = null.StringFrom(address.LocalPart) + _, err = v.user.GetUser(c.Request().Context(), u) + } log.Printf("failed login for \"%s\": %v", u.Username, err) err = session.Save(c.Request(), c.Response()) if err != nil { @@ -110,6 +115,14 @@ func (v *Views) LoginFunc(c echo.Context) error { session.Values["user"] = u + c.SetCookie(&http.Cookie{ + Name: "test-pass", + Value: "hello", + MaxAge: 60, + Secure: false, + HttpOnly: false, + }) + if c.FormValue("remember") != "on" { session.Options.MaxAge = 86400 * 31 } @@ -124,3 +137,71 @@ func (v *Views) LoginFunc(c echo.Context) error { } return fmt.Errorf("failed to parse method") } + +//func (v *Views) LDAPFunc(username, password string) (bool, error) { +// config := &auth.Config{ +// Server: v.conf.AD.Server, +// Port: v.conf.AD.Port, +// BaseDN: v.conf.AD.BaseDN, +// Security: auth.SecurityType(v.conf.AD.Security), +// } +// +// conn, err := config.Connect() +// if err != nil { +// return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("error connecting to server: %w", err)) +// } +// defer func(Conn *ldap.Conn) { +// err = Conn.Close() +// if err != nil { +// log.Printf("failed to close to LDAP server: %+v", err) +// } +// }(conn.Conn) +// +// status, err := conn.Bind(v.conf.AD.Bind.Username, v.conf.AD.Bind.Password) +// if err != nil { +// return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("error binding to server: %w", err)) +// } +// +// if !status { +// return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("error binding to server: invalid credentials")) +// } +// +// status1, err := auth.Authenticate(config, username, password) +// if err != nil { +// return false, echo.NewHTTPError(http.StatusUnauthorized, fmt.Errorf("unable to authenticate %s with error: %w", username, err)) +// } +// +// if status1 { +// var entry *ldap.Entry +// if _, err = mail.ParseAddress(username); err == nil { +// entry, err = conn.GetAttributes("userPrincipalName", username, []string{"memberOf"}) +// } else { +// entry, err = conn.GetAttributes("samAccountName", username, []string{"memberOf"}) +// } +// if err != nil { +// return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("error getting user groups: %w", err)) +// } +// +// dnGroups := entry.GetAttributeValues("memberOf") +// +// if len(dnGroups) == 0 { +// return false, echo.NewHTTPError(http.StatusUnauthorized, fmt.Errorf("BIND_SAM user not member of any groups")) +// } +// +// //stv := false +// +// for _, group := range dnGroups { +// if group == "CN=STV Admin,CN=Users,DC=ystv,DC=local" { +// //stv = true +// return true, nil +// } +// } +// +// //if !stv { +// // return false, echo.NewHTTPError(http.StatusUnauthorized, fmt.Errorf("STV not allowed for %s!\n", username)) +// //} +// log.Printf("%s is authenticated", username) +// return true, nil +// } +// return false, echo.NewHTTPError(http.StatusUnauthorized, fmt.Errorf("user not authenticated: %s", username)) +//} diff --git a/views/views.go b/views/views.go index 26e8b7b..d1889df 100644 --- a/views/views.go +++ b/views/views.go @@ -34,6 +34,20 @@ type ( SessionCookieName string Mail SMTPConfig Security SecurityConfig + AD ADConfig + } + + ADConfig struct { + Server string + Port int + BaseDN string + Security int + Bind ADBind + } + + ADBind struct { + Username string + Password string } // SMTPConfig stores the SMTP Mailer configuration From b4e8f2ee7bfa53d8d8f9bed2d9104d187275d6c8 Mon Sep 17 00:00:00 2001 From: Liam Burnand Date: Sun, 16 Feb 2025 21:25:31 +0000 Subject: [PATCH 2/3] Updating packages --- go.mod | 3 ++- go.sum | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 0f915f6..dc986c5 100644 --- a/go.mod +++ b/go.mod @@ -22,11 +22,13 @@ require ( github.com/joho/godotenv v1.5.1 github.com/labstack/echo/v4 v4.13.3 github.com/lib/pq v1.10.9 + github.com/mcnijman/go-emailaddress v1.1.1 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pressly/goose/v3 v3.24.1 github.com/stretchr/testify v1.10.0 github.com/xhit/go-simple-mail/v2 v2.16.0 go.uber.org/mock v0.5.0 + golang.org/x/crypto v0.33.0 gopkg.in/guregu/null.v4 v4.0.0 ) @@ -57,7 +59,6 @@ require ( github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/crypto v0.33.0 // indirect golang.org/x/net v0.35.0 // indirect golang.org/x/sync v0.11.0 // indirect golang.org/x/sys v0.30.0 // indirect diff --git a/go.sum b/go.sum index 39276c9..573f04b 100644 --- a/go.sum +++ b/go.sum @@ -88,6 +88,8 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/mcnijman/go-emailaddress v1.1.1 h1:AGhgVDG3tCDaL0/Vc6erlPQjDuDN3dAT7rRdgFtetr0= +github.com/mcnijman/go-emailaddress v1.1.1/go.mod h1:5whZrhS8Xp5LxO8zOD35BC+b76kROtsh+dPomeRt/II= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= @@ -143,6 +145,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= From 962bad15c85a5b7f3a8ea111b1f157dce619253c Mon Sep 17 00:00:00 2001 From: Liam Burnand Date: Sun, 16 Feb 2025 21:39:17 +0000 Subject: [PATCH 3/3] Adding back in the function --- go.mod | 3 + go.sum | 9 +++ views/api.go | 79 +++++++++++++------------- views/login.go | 147 ++++++++++++++++++++++++++----------------------- 4 files changed, 130 insertions(+), 108 deletions(-) diff --git a/go.mod b/go.mod index dc986c5..35c9d86 100644 --- a/go.mod +++ b/go.mod @@ -20,6 +20,7 @@ require ( github.com/jinzhu/copier v0.4.0 github.com/jmoiron/sqlx v1.4.0 github.com/joho/godotenv v1.5.1 + github.com/korylprince/go-ad-auth/v3 v3.3.0 github.com/labstack/echo/v4 v4.13.3 github.com/lib/pq v1.10.9 github.com/mcnijman/go-emailaddress v1.1.1 @@ -39,6 +40,8 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/go-asn1-ber/asn1-ber v1.4.1 // indirect + github.com/go-ldap/ldap/v3 v3.1.7 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-test/deep v1.1.1 // indirect diff --git a/go.sum b/go.sum index 573f04b..d26c6da 100644 --- a/go.sum +++ b/go.sum @@ -23,6 +23,11 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/go-asn1-ber/asn1-ber v1.3.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-asn1-ber/asn1-ber v1.4.1 h1:qP/QDxOtmMoJVgXHCXNzDpA0+wkgYB2x5QoLMVOciyw= +github.com/go-asn1-ber/asn1-ber v1.4.1/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= +github.com/go-ldap/ldap/v3 v3.1.7 h1:aHjuWTgZsnxjMgqzx0JHwNqz4jBYZTcNarbPFkW1Oww= +github.com/go-ldap/ldap/v3 v3.1.7/go.mod h1:5Zun81jBTabRaI8lzN7E1JjyEl1g6zI6u9pd8luAK4Q= github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= @@ -70,6 +75,8 @@ github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/korylprince/go-ad-auth/v3 v3.3.0 h1:iXuB+sCk4GniHnpUn0BAHH8rKeOLTKuYcBNvERa773Y= +github.com/korylprince/go-ad-auth/v3 v3.3.0/go.mod h1:19M0geaOeNN489k1MO6GCqOCgbruYRQkHRBfhhUZAoE= github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= @@ -160,10 +167,12 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/genproto/googleapis/rpc v0.0.0-20250212204824-5a70512c5d8b h1:FQtJ1MxbXoIIrZHZ33M+w5+dAP9o86rgpjoKr/ZmT7k= diff --git a/views/api.go b/views/api.go index a8da3b0..5b42f74 100644 --- a/views/api.go +++ b/views/api.go @@ -8,7 +8,6 @@ import ( "fmt" "log" "net/http" - "net/url" "strings" "time" @@ -235,45 +234,45 @@ func (v *Views) SetTokenHandler(c echo.Context) error { } /* - _ = tokenByte - - callback := "" - callbackURL, err := url.Parse(c.QueryParam("callback")) - fmt.Println(callbackURL.String(), err) - if err == nil && strings.HasSuffix(callbackURL.Host, v.conf.BaseDomainName) && callbackURL.String() != "" { - callback = callbackURL.String() - } - // c.Response().Header().Set("Content-Type", "application/json") - c.Response().Header().Set("Authorization", "Bearer "+tokenString) - cookie := new(http.Cookie) - cookie.Name = "token" - cookie.Expires = time.Now().Add(30 * time.Second) - cookie.Value = tokenString - cookie.Secure = false - cookie.HttpOnly = false - cookie.Domain = "localhost" - c.SetCookie(cookie) - http.SetCookie(c.Response().Writer, cookie) - c.Response().Committed = false - // c.Response().Write(tokenByte) - // _, err = c.Response().Write(tokenByte) - // if err != nil { - // log.Printf("failed to write token to http body: %+v", err) - // data := struct { - // Error error `json:"error"` - // }{ - // Error: fmt.Errorf("failed to write token to http body: %w", err), - // } - // return c.JSON(http.StatusInternalServerError, data) - // } - if len(callback) > 0 { - // c.Response().Header().Set("Location", callback) - // c.Response().WriteHeader(http.StatusFound) - return c.Redirect(http.StatusFound, callback+"?token="+tokenString) - // c.Redirect() - } - return nil - */ + _ = tokenByte + + callback := "" + callbackURL, err := url.Parse(c.QueryParam("callback")) + fmt.Println(callbackURL.String(), err) + if err == nil && strings.HasSuffix(callbackURL.Host, v.conf.BaseDomainName) && callbackURL.String() != "" { + callback = callbackURL.String() + } + // c.Response().Header().Set("Content-Type", "application/json") + c.Response().Header().Set("Authorization", "Bearer "+tokenString) + cookie := new(http.Cookie) + cookie.Name = "token" + cookie.Expires = time.Now().Add(30 * time.Second) + cookie.Value = tokenString + cookie.Secure = false + cookie.HttpOnly = false + cookie.Domain = "localhost" + c.SetCookie(cookie) + http.SetCookie(c.Response().Writer, cookie) + c.Response().Committed = false + // c.Response().Write(tokenByte) + // _, err = c.Response().Write(tokenByte) + // if err != nil { + // log.Printf("failed to write token to http body: %+v", err) + // data := struct { + // Error error `json:"error"` + // }{ + // Error: fmt.Errorf("failed to write token to http body: %w", err), + // } + // return c.JSON(http.StatusInternalServerError, data) + // } + if len(callback) > 0 { + // c.Response().Header().Set("Location", callback) + // c.Response().WriteHeader(http.StatusFound) + return c.Redirect(http.StatusFound, callback+"?token="+tokenString) + // c.Redirect() + } + return nil + */ c.Response().Header().Set("Content-Type", "application/json") c.Response().WriteHeader(http.StatusCreated) diff --git a/views/login.go b/views/login.go index 390b32c..abfb529 100644 --- a/views/login.go +++ b/views/login.go @@ -1,13 +1,17 @@ package views import ( + "errors" "fmt" "log" "net/http" + "net/mail" "net/url" "strings" + "github.com/go-ldap/ldap/v3" "github.com/google/uuid" + auth "github.com/korylprince/go-ad-auth/v3" "github.com/labstack/echo/v4" emailParser "github.com/mcnijman/go-emailaddress" "github.com/patrickmn/go-cache" @@ -80,7 +84,19 @@ func (v *Views) _loginPost(c echo.Context) error { address, _ := emailParser.Parse(username) if address != nil { u.LDAPUsername = null.StringFrom(address.LocalPart) - _, err = v.user.GetUser(c.Request().Context(), u) + ldapUser, err := v.user.GetUser(c.Request().Context(), u) + if err != nil { + return fmt.Errorf("failed to get user ldap: %w", err) + } + if !ldapUser.LDAPUsername.Valid { + return errors.New("failed to get user LDAP username") + } + valid, err := v.LDAPFunc(ldapUser.LDAPUsername.String, password) + if err != nil { + return fmt.Errorf("failed to call LDAP function: %w", err) + } + + fmt.Println("LDAP: ", valid) } log.Printf("failed login for \"%s\": %v", u.Username, err) @@ -154,70 +170,65 @@ func (v *Views) _loginPost(c echo.Context) error { return c.Redirect(http.StatusFound, callback) } -//func (v *Views) LDAPFunc(username, password string) (bool, error) { -// config := &auth.Config{ -// Server: v.conf.AD.Server, -// Port: v.conf.AD.Port, -// BaseDN: v.conf.AD.BaseDN, -// Security: auth.SecurityType(v.conf.AD.Security), -// } -// -// conn, err := config.Connect() -// if err != nil { -// return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("error connecting to server: %w", err)) -// } -// defer func(Conn *ldap.Conn) { -// err = Conn.Close() -// if err != nil { -// log.Printf("failed to close to LDAP server: %+v", err) -// } -// }(conn.Conn) -// -// status, err := conn.Bind(v.conf.AD.Bind.Username, v.conf.AD.Bind.Password) -// if err != nil { -// return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("error binding to server: %w", err)) -// } -// -// if !status { -// return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("error binding to server: invalid credentials")) -// } -// -// status1, err := auth.Authenticate(config, username, password) -// if err != nil { -// return false, echo.NewHTTPError(http.StatusUnauthorized, fmt.Errorf("unable to authenticate %s with error: %w", username, err)) -// } -// -// if status1 { -// var entry *ldap.Entry -// if _, err = mail.ParseAddress(username); err == nil { -// entry, err = conn.GetAttributes("userPrincipalName", username, []string{"memberOf"}) -// } else { -// entry, err = conn.GetAttributes("samAccountName", username, []string{"memberOf"}) -// } -// if err != nil { -// return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("error getting user groups: %w", err)) -// } -// -// dnGroups := entry.GetAttributeValues("memberOf") -// -// if len(dnGroups) == 0 { -// return false, echo.NewHTTPError(http.StatusUnauthorized, fmt.Errorf("BIND_SAM user not member of any groups")) -// } -// -// //stv := false -// -// for _, group := range dnGroups { -// if group == "CN=STV Admin,CN=Users,DC=ystv,DC=local" { -// //stv = true -// return true, nil -// } -// } -// -// //if !stv { -// // return false, echo.NewHTTPError(http.StatusUnauthorized, fmt.Errorf("STV not allowed for %s!\n", username)) -// //} -// log.Printf("%s is authenticated", username) -// return true, nil -// } -// return false, echo.NewHTTPError(http.StatusUnauthorized, fmt.Errorf("user not authenticated: %s", username)) -//} +func (v *Views) LDAPFunc(username, password string) (bool, error) { + config := &auth.Config{ + Server: v.conf.AD.Server, + Port: v.conf.AD.Port, + BaseDN: v.conf.AD.BaseDN, + Security: auth.SecurityType(v.conf.AD.Security), + } + + conn, err := config.Connect() + if err != nil { + return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("error connecting to server: %w", err)) + } + defer conn.Conn.Close() + + status, err := conn.Bind(v.conf.AD.Bind.Username, v.conf.AD.Bind.Password) + if err != nil { + return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("error binding to server: %w", err)) + } + + if !status { + return false, echo.NewHTTPError(http.StatusInternalServerError, errors.New("error binding to server: invalid credentials")) + } + + status1, err := auth.Authenticate(config, username, password) + if err != nil { + return false, echo.NewHTTPError(http.StatusUnauthorized, fmt.Errorf("unable to authenticate %s with error: %w", username, err)) + } + + if status1 { + var entry *ldap.Entry + if _, err = mail.ParseAddress(username); err == nil { + entry, err = conn.GetAttributes("userPrincipalName", username, []string{"memberOf"}) + } else { + entry, err = conn.GetAttributes("samAccountName", username, []string{"memberOf"}) + } + if err != nil { + return false, echo.NewHTTPError(http.StatusInternalServerError, fmt.Errorf("error getting user groups: %w", err)) + } + + dnGroups := entry.GetAttributeValues("memberOf") + + if len(dnGroups) == 0 { + return false, echo.NewHTTPError(http.StatusUnauthorized, errors.New("BIND_SAM user not member of any groups")) + } + + // stv := false + + for _, group := range dnGroups { + if group == "CN=STV Admin,CN=Users,DC=ystv,DC=local" { + // stv = true + return true, nil + } + } + + // if !stv { + // return false, echo.NewHTTPError(http.StatusUnauthorized, fmt.Errorf("STV not allowed for %s!\n", username)) + // } + log.Printf("%s is authenticated", username) + return true, nil + } + return false, echo.NewHTTPError(http.StatusUnauthorized, fmt.Errorf("user not authenticated: %s", username)) +}