Newer
Older
wg-portal / internal / server / handlers_auth.go
@Christoph Haas Christoph Haas on 23 Mar 2021 4 KB add csrf
package server

import (
	"net/http"
	"strings"

	csrf "github.com/utrack/gin-csrf"

	"github.com/gin-gonic/gin"
	"github.com/h44z/wg-portal/internal/authentication"
	"github.com/h44z/wg-portal/internal/users"
	"github.com/sirupsen/logrus"
)

func (s *Server) GetLogin(c *gin.Context) {
	currentSession := GetSessionData(c)
	if currentSession.LoggedIn {
		c.Redirect(http.StatusSeeOther, "/") // already logged in
	}

	authError := c.DefaultQuery("err", "")
	errMsg := "Unknown error occurred, try again!"
	switch authError {
	case "missingdata":
		errMsg = "Invalid login data retrieved, please fill out all fields and try again!"
	case "authfail":
		errMsg = "Authentication failed!"
	case "loginreq":
		errMsg = "Login required!"
	}

	c.HTML(http.StatusOK, "login.html", gin.H{
		"error":   authError != "",
		"message": errMsg,
		"static":  s.getStaticData(),
		"Csrf":    csrf.GetToken(c),
	})
}

func (s *Server) PostLogin(c *gin.Context) {
	currentSession := GetSessionData(c)
	if currentSession.LoggedIn {
		// already logged in
		c.Redirect(http.StatusSeeOther, "/")
		return
	}

	username := strings.ToLower(c.PostForm("username"))
	password := c.PostForm("password")

	// Validate form input
	if strings.Trim(username, " ") == "" || strings.Trim(password, " ") == "" {
		c.Redirect(http.StatusSeeOther, "/auth/login?err=missingdata")
		return
	}

	// Check user database for an matching entry
	var loginProvider authentication.AuthProvider
	email := ""
	user := s.users.GetUser(username) // retrieve active candidate user from db
	if user != nil {                  // existing user
		loginProvider = s.auth.GetProvider(string(user.Source))
		if loginProvider == nil {
			s.GetHandleError(c, http.StatusInternalServerError, "login error", "login provider unavailable")
			return
		}
		authEmail, err := loginProvider.Login(&authentication.AuthContext{
			Username: username,
			Password: password,
		})
		if err == nil {
			email = authEmail
		}
	} else { // possible new user
		// Check all available auth backends
		for _, provider := range s.auth.GetProvidersForType(authentication.AuthProviderTypePassword) {
			// try to log in to the given provider
			authEmail, err := provider.Login(&authentication.AuthContext{
				Username: username,
				Password: password,
			})
			if err != nil {
				continue
			}

			email = authEmail
			loginProvider = provider

			// create new user in the database (or reactivate him)
			userData, err := loginProvider.GetUserModel(&authentication.AuthContext{
				Username: email,
			})
			if err != nil {
				s.GetHandleError(c, http.StatusInternalServerError, "login error", err.Error())
				return
			}
			if err := s.CreateUser(users.User{
				Email:     userData.Email,
				Source:    users.UserSource(loginProvider.GetName()),
				IsAdmin:   userData.IsAdmin,
				Firstname: userData.Firstname,
				Lastname:  userData.Lastname,
				Phone:     userData.Phone,
			}, s.wg.Cfg.GetDefaultDeviceName()); err != nil {
				s.GetHandleError(c, http.StatusInternalServerError, "login error", "failed to update user data")
				return
			}

			user = s.users.GetUser(username)
			break
		}
	}

	// Check if user is authenticated
	if email == "" || loginProvider == nil || user == nil {
		c.Redirect(http.StatusSeeOther, "/auth/login?err=authfail")
		return
	}

	// Set authenticated session
	sessionData := GetSessionData(c)
	sessionData.LoggedIn = true
	sessionData.IsAdmin = user.IsAdmin
	sessionData.Email = user.Email
	sessionData.Firstname = user.Firstname
	sessionData.Lastname = user.Lastname
	sessionData.DeviceName = s.wg.Cfg.DeviceNames[0]

	// Check if user already has a peer setup, if not create one
	if err := s.CreateUserDefaultPeer(user.Email, s.wg.Cfg.GetDefaultDeviceName()); err != nil {
		// Not a fatal error, just log it...
		logrus.Errorf("failed to automatically create vpn peer for %s: %v", sessionData.Email, err)
	}

	if err := UpdateSessionData(c, sessionData); err != nil {
		s.GetHandleError(c, http.StatusInternalServerError, "login error", "failed to save session")
		return
	}
	c.Redirect(http.StatusSeeOther, "/")
}

func (s *Server) GetLogout(c *gin.Context) {
	currentSession := GetSessionData(c)

	if !currentSession.LoggedIn { // Not logged in
		c.Redirect(http.StatusSeeOther, "/")
		return
	}

	if err := DestroySessionData(c); err != nil {
		s.GetHandleError(c, http.StatusInternalServerError, "logout error", "failed to destroy session")
		return
	}
	c.Redirect(http.StatusSeeOther, "/")
}