auth-ui: add e2e tests

Change-Id: Ic8f2f9e032d24eed2d4fd824dcfc26c59d7d915e
diff --git a/core/auth/ui/main.go b/core/auth/ui/main.go
index 73561e7..978ddc7 100644
--- a/core/auth/ui/main.go
+++ b/core/auth/ui/main.go
@@ -134,8 +134,8 @@
 	s.r.Path("/consent").Methods(http.MethodGet).HandlerFunc(s.consent)
 	s.r.Path("/consent").Methods(http.MethodPost).HandlerFunc(s.processConsent)
 	s.r.Path("/logout").Methods(http.MethodGet).HandlerFunc(s.logout)
-	s.r.Path("/change-password").Methods("POST").HandlerFunc(s.changePassword)
-	s.r.Path("/change-password").Methods("GET").HandlerFunc(s.changePasswordForm)
+	s.r.Path("/settings").Methods("POST").HandlerFunc(s.changePassword)
+	s.r.Path("/settings").Methods("GET").HandlerFunc(s.changePasswordForm)
 	s.r.Path("/").HandlerFunc(s.whoami)
 	return s.serv.ListenAndServe()
 }
@@ -593,20 +593,28 @@
 type changePasswordData struct {
 	Username       string
 	Password       string
+	CSRFToken      string
+	FormAction     string
 	PasswordErrors []ValidationError
 }
 
 func (s *Server) changePasswordForm(w http.ResponseWriter, r *http.Request) {
-	_, username, err := getWhoAmIFromKratos(r.Cookies())
-	if err != nil {
-		if errors.Is(err, ErrNotLoggedIn) {
-			http.Redirect(w, r, "/", http.StatusSeeOther)
-		} else {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-		}
+	flow := r.FormValue("flow")
+	if flow == "" {
+		http.Redirect(w, r, s.kratos+"/self-service/settings/browser", http.StatusSeeOther)
 		return
 	}
-	if err := s.tmpls.ChangePassword.Execute(w, changePasswordData{Username: username}); err != nil {
+	_, username, err := getWhoAmIFromKratos(r.Cookies())
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	csrfToken, err := getCSRFToken("settings", flow, r.Cookies())
+	if err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	if err := s.tmpls.ChangePassword.Execute(w, changePasswordData{Username: username, CSRFToken: csrfToken, FormAction: r.URL.Path + "?flow=" + url.QueryEscape(flow)}); err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
 		return
 	}
@@ -617,28 +625,46 @@
 		http.Error(w, err.Error(), http.StatusBadRequest)
 		return
 	}
+	flow := r.FormValue("flow")
+	if flow == "" {
+		http.Redirect(w, r, s.kratos+"/self-service/settings/browser", http.StatusSeeOther)
+		return
+	}
 	password := r.FormValue("password")
-	id, username, err := getWhoAmIFromKratos(r.Cookies())
+	_, username, err := getWhoAmIFromKratos(r.Cookies())
 	if err != nil {
-		if errors.Is(err, ErrNotLoggedIn) {
-			http.Redirect(w, r, "/", http.StatusSeeOther)
-		} else {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	if verr := validatePassword(password); len(verr) > 0 {
+		if err := s.tmpls.ChangePassword.Execute(w, changePasswordData{Username: username, Password: password, CSRFToken: r.FormValue("csrf_token"), FormAction: r.URL.Path + "?flow=" + url.QueryEscape(flow), PasswordErrors: verr}); err != nil {
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 		}
 		return
 	}
-	if verr, err := s.api.apiPasswordChange(id, username, password); err != nil {
+	resp, err := postFormToKratos("settings", flow, r.Cookies(), url.Values{
+		"csrf_token": {r.FormValue("csrf_token")},
+		"method":     {"password"},
+		"password":   {password},
+	})
+	if err != nil {
 		http.Error(w, err.Error(), http.StatusInternalServerError)
-	} else if len(verr) > 0 {
-		if err := s.tmpls.ChangePassword.Execute(w, changePasswordData{username, password, verr}); err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
+	}
+	defer resp.Body.Close()
+	if resp.StatusCode >= http.StatusBadRequest {
+		if err := extractError(resp.Body); err != nil {
+			if renderErr := s.tmpls.ChangePassword.Execute(w, changePasswordData{Username: username, Password: password, CSRFToken: r.FormValue("csrf_token"), FormAction: r.URL.Path + "?flow=" + url.QueryEscape(flow), PasswordErrors: []ValidationError{{Field: "password", Message: err.Error()}}}); renderErr != nil {
+				http.Error(w, renderErr.Error(), http.StatusInternalServerError)
+			}
 			return
 		}
-	} else {
-		if err := s.tmpls.ChangePasswordSuccess.Execute(w, nil); err != nil {
-			http.Error(w, err.Error(), http.StatusInternalServerError)
-			return
-		}
+		http.Error(w, "password change failed", resp.StatusCode)
+		return
+	}
+	if err := s.tmpls.ChangePasswordSuccess.Execute(w, nil); err != nil {
+		http.Error(w, err.Error(), http.StatusInternalServerError)
+		return
 	}
 }