diff --git a/.gitignore b/.gitignore index a1338d6..92027e5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ # Project-local glide cache, RE: https://github.com/Masterminds/glide/issues/736 .glide/ +vendor/ +*.lock diff --git a/Gopkg.toml b/Gopkg.toml new file mode 100644 index 0000000..daef9f1 --- /dev/null +++ b/Gopkg.toml @@ -0,0 +1,29 @@ + +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + + +[[constraint]] + name = "gopkg.in/ldap.v2" + version = "2.5.1" +[[constraint]] + name = "github.com/dchest/captcha" + branch = "master" \ No newline at end of file diff --git a/Makefile b/Makefile index 7e09867..b8e9ddf 100644 --- a/Makefile +++ b/Makefile @@ -3,8 +3,11 @@ VER=1.2 .PHONY: all build push -all: build docker push clean +all: init build docker push clean +init: + dep ensure + build: GOOS=linux go build -o ldap-pass-webui main.go diff --git a/README.md b/README.md index a79e493..bb0f968 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,29 @@ WebUI Client capable of connecting to backend LDAP server and changing the users ![Screenshot](screenshots/index.png) -## Running in docker container +The configuration is made with environment variables: + +|Env variable|Default value|Description| +|------------|-------------|-----------| +|LPW_TITLE|Change your global password for example.org|Title that will appear on the page| +|LPW_HOST||LDAP Host to connect to| +|LPW_PORT|636|LDAP Port (389|636 are default LDAP/LDAPS)| +|LPW_ENCRYPTED|true|Use enrypted communication| +|LPW_START_TLS|false|Start TLS communication| +|LPW_SSL_SKIP_VERIFY|true|Skip TLS CA verification| +|LPW_USER_DN|uid=%s,ou=people,dc=example,dc=org|Filter expression to search the user for Binding| +|LPW_USER_BASE|ou=people,dc=example,dc=org|Base to use when doing the binding| + +## Running + +```sh +dep ensure +LPW_HOST=ldap_host_ip go run main.go +``` + +Browse [http://localhost:8080/](http://localhost:8080/) + +### Running in docker container ```sh docker run -d -p 8080:8080 --name ldap-passwd-webui \ @@ -23,6 +45,11 @@ docker run -d -p 8080:8080 --name ldap-passwd-webui \ ## Building and tagging +Get [Godep](https://github.com/golang/dep) +```sh +go get -u github.com/golang/dep/cmd/dep +``` + ```sh make ``` @@ -30,4 +57,5 @@ make ## Credits * [Web UI for changing LDAP password - python](https://github.com/jirutka/ldap-passwd-webui) - * [Gitea](https://github.com/go-gitea/gitea) \ No newline at end of file + * [Gitea](https://github.com/go-gitea/gitea) + * [dchest/captcha](https://github.com/dchest/captcha) \ No newline at end of file diff --git a/app/web.go b/app/web.go index 022b710..eb3d888 100644 --- a/app/web.go +++ b/app/web.go @@ -8,6 +8,8 @@ import ( "html/template" + "github.com/dchest/captcha" + "regexp" "net/http" @@ -19,14 +21,17 @@ type route struct { handler http.Handler } +// RegexpHandler is used for http handler to bind using regular expressions type RegexpHandler struct { routes []*route } +// Handler binds http handler on RegexpHandler func (h *RegexpHandler) Handler(pattern *regexp.Regexp, verb string, handler http.Handler) { h.routes = append(h.routes, &route{pattern, verb, handler}) } +// HandleFunc binds http handler function on RegexpHandler func (h *RegexpHandler) HandleFunc(r string, v string, handler func(http.ResponseWriter, *http.Request)) { re := regexp.MustCompile(r) h.routes = append(h.routes, &route{re, v, http.HandlerFunc(handler)}) @@ -48,6 +53,7 @@ type pageData struct { PatternInfo string Username string Alerts map[string]string + CaptchaId string } // ServeAssets : Serves the static assets @@ -57,7 +63,7 @@ func ServeAssets(w http.ResponseWriter, req *http.Request) { // ServeIndex : Serves index page on GET request func ServeIndex(w http.ResponseWriter, req *http.Request) { - p := &pageData{Title: getTitle(), Pattern: getPattern(), PatternInfo: getPatternInfo()} + p := &pageData{Title: getTitle(), CaptchaId: captcha.New(), Pattern: getPattern(), PatternInfo: getPatternInfo()} t, e := template.ParseFiles(path.Join("templates", "index.html")) if e != nil { log.Printf("Error parsing file %v\n", e) @@ -74,6 +80,8 @@ func ChangePassword(w http.ResponseWriter, req *http.Request) { oldPassword := req.Form["old-password"] newPassword := req.Form["new-password"] confirmPassword := req.Form["confirm-password"] + captchaID := req.Form["captchaId"] + captchaSolution := req.Form["captchaSolution"] alerts := map[string]string{} @@ -100,6 +108,12 @@ func ChangePassword(w http.ResponseWriter, req *http.Request) { alerts["error"] = alerts["error"] + fmt.Sprintf("%s", getPatternInfo()) } + if len(captchaID) < 1 || captchaID[0] == "" || + len(captchaSolution) < 1 || captchaSolution[0] == "" || + !captcha.VerifyString(captchaID[0], captchaSolution[0]) { + alerts["error"] = "Wrong captcha." + } + if len(alerts) == 0 { client := NewLDAPClient() if err := client.ModifyPassword(un, oldPassword[0], newPassword[0]); err != nil { @@ -109,7 +123,7 @@ func ChangePassword(w http.ResponseWriter, req *http.Request) { } } - p := &pageData{Title: getTitle(), Alerts: alerts, Username: un} + p := &pageData{Title: getTitle(), Alerts: alerts, Username: un, CaptchaId: captcha.New()} t, e := template.ParseFiles(path.Join("templates", "index.html")) if e != nil { diff --git a/main.go b/main.go index 3babe99..a234633 100644 --- a/main.go +++ b/main.go @@ -2,8 +2,10 @@ package main import ( "fmt" - "github.com/npenkov/ldap-passwd-webui/app" "net/http" + + "github.com/dchest/captcha" + "github.com/npenkov/ldap-passwd-webui/app" ) func main() { @@ -12,7 +14,8 @@ func main() { reHandler.HandleFunc(".*.[js|css|png|eof|svg|ttf|woff]", "GET", app.ServeAssets) reHandler.HandleFunc("/", "GET", app.ServeIndex) reHandler.HandleFunc("/", "POST", app.ChangePassword) - + http.Handle("/captcha/", captcha.Server(captcha.StdWidth, captcha.StdHeight)) + http.Handle("/", reHandler) fmt.Println("Starting server on port 8080") - http.ListenAndServe(":8080", reHandler) + http.ListenAndServe(":8080", nil) } diff --git a/screenshots/index.png b/screenshots/index.png index a4fd472..6719cb2 100644 Binary files a/screenshots/index.png and b/screenshots/index.png differ diff --git a/templates/index.html b/templates/index.html index 9e3d357..f7708c4 100644 --- a/templates/index.html +++ b/templates/index.html @@ -12,6 +12,36 @@ +

{{.Title}}

@@ -28,8 +58,16 @@ + pattern="{{.Pattern}}" x-moz-errormessage="{{.PatternInfo}}" required>

{{.PatternInfo}}

+

Type the numbers you see in the picture below:

+

+ Captcha image +

+ Reload + +
+