wg-portal/internal/server/core.go

296 lines
7.3 KiB
Go
Raw Normal View History

2020-11-05 13:37:51 -05:00
package server
import (
2021-02-22 16:25:08 -05:00
"context"
2020-11-05 13:37:51 -05:00
"encoding/gob"
"errors"
2020-11-09 14:26:34 -05:00
"html/template"
2021-01-13 11:27:01 -05:00
"io/ioutil"
2020-11-05 13:37:51 -05:00
"math/rand"
2020-11-10 16:23:05 -05:00
"net/url"
2020-11-05 13:37:51 -05:00
"os"
"path/filepath"
"time"
2020-11-09 14:26:34 -05:00
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/memstore"
2020-11-05 13:37:51 -05:00
"github.com/gin-gonic/gin"
2021-02-08 16:56:02 -05:00
"github.com/h44z/wg-portal/internal/common"
"github.com/h44z/wg-portal/internal/ldap"
"github.com/h44z/wg-portal/internal/wireguard"
"github.com/sirupsen/logrus"
ginlogrus "github.com/toorop/gin-logrus"
2020-11-05 13:37:51 -05:00
)
const SessionIdentifier = "wgPortalSession"
const CacheRefreshDuration = 5 * time.Minute
func init() {
gob.Register(SessionData{})
2020-11-10 16:23:05 -05:00
gob.Register(FlashData{})
2021-02-21 17:23:58 -05:00
gob.Register(Peer{})
2020-11-09 05:06:02 -05:00
gob.Register(Device{})
gob.Register(LdapCreateForm{})
2020-11-05 13:37:51 -05:00
}
type SessionData struct {
LoggedIn bool
IsAdmin bool
UID string
UserName string
Firstname string
Lastname string
Email string
2020-11-05 13:37:51 -05:00
SortedBy string
SortDirection string
Search string
AlertData string
AlertType string
2020-11-07 12:36:23 -05:00
FormData interface{}
2020-11-05 13:37:51 -05:00
}
2020-11-10 16:23:05 -05:00
type FlashData struct {
2020-11-05 13:37:51 -05:00
HasAlert bool
Message string
Type string
}
type StaticData struct {
WebsiteTitle string
WebsiteLogo string
2020-11-10 03:31:02 -05:00
CompanyName string
Year int
LdapDisabled bool
2020-11-05 13:37:51 -05:00
}
type Server struct {
// Core components
2021-02-22 16:25:08 -05:00
ctx context.Context
2020-11-09 14:26:34 -05:00
config *common.Config
server *gin.Engine
users *UserManager
mailTpl *template.Template
2020-11-05 13:37:51 -05:00
// WireGuard stuff
wg *wireguard.Manager
// LDAP stuff
2020-11-10 03:31:02 -05:00
ldapDisabled bool
2020-11-05 13:37:51 -05:00
ldapAuth ldap.Authentication
ldapUsers *ldap.SynchronizedUserCacheHolder
ldapCacheUpdater *ldap.UserCache
}
2021-02-22 16:25:08 -05:00
func (s *Server) Setup(ctx context.Context) error {
2020-11-10 03:57:49 -05:00
dir := s.getExecutableDirectory()
rDir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
2021-02-08 16:56:02 -05:00
logrus.Infof("Real working directory: %s", rDir)
logrus.Infof("Current working directory: %s", dir)
2020-11-10 03:57:49 -05:00
2020-11-05 13:37:51 -05:00
// Init rand
rand.Seed(time.Now().UnixNano())
s.config = common.NewConfig()
// Setup LDAP stuff
s.ldapAuth = ldap.NewAuthentication(s.config.LDAP)
s.ldapUsers = &ldap.SynchronizedUserCacheHolder{}
s.ldapUsers.Init()
s.ldapCacheUpdater = ldap.NewUserCache(s.config.LDAP, s.ldapUsers)
if s.ldapCacheUpdater.LastError != nil {
2021-02-08 16:56:02 -05:00
logrus.Warnf("LDAP error: %v", s.ldapCacheUpdater.LastError)
logrus.Warnf("LDAP features disabled!")
2020-11-10 03:31:02 -05:00
s.ldapDisabled = true
2020-11-05 13:37:51 -05:00
}
// Setup WireGuard stuff
s.wg = &wireguard.Manager{Cfg: &s.config.WG}
if err := s.wg.Init(); err != nil {
return err
}
// Setup user manager
2020-11-10 03:57:49 -05:00
if s.users = NewUserManager(filepath.Join(dir, s.config.Core.DatabasePath), s.wg, s.ldapUsers); s.users == nil {
2020-11-05 13:37:51 -05:00
return errors.New("unable to setup user manager")
}
2020-11-07 04:31:48 -05:00
if err := s.users.InitFromCurrentInterface(); err != nil {
return errors.New("unable to initialize user manager")
}
if err := s.RestoreWireGuardInterface(); err != nil {
2021-02-08 16:56:02 -05:00
return errors.New("unable to restore WireGuard state")
}
2020-11-05 13:37:51 -05:00
2020-11-10 03:57:49 -05:00
// Setup mail template
2020-11-09 14:26:34 -05:00
var err error
s.mailTpl, err = template.New("email.html").ParseFiles(filepath.Join(dir, "/assets/tpl/email.html"))
2020-11-09 14:26:34 -05:00
if err != nil {
return errors.New("unable to pare mail template")
}
2020-11-05 13:37:51 -05:00
// Setup http server
2021-01-13 11:27:01 -05:00
gin.SetMode(gin.ReleaseMode)
gin.DefaultWriter = ioutil.Discard
s.server = gin.New()
2021-02-08 16:56:02 -05:00
s.server.Use(ginlogrus.Logger(logrus.StandardLogger()), gin.Recovery())
2020-11-10 16:23:05 -05:00
s.server.SetFuncMap(template.FuncMap{
"formatBytes": common.ByteCountSI,
"urlEncode": url.QueryEscape,
})
2020-11-05 13:37:51 -05:00
// Setup templates
2021-02-08 16:56:02 -05:00
logrus.Infof("Loading templates from: %s", filepath.Join(dir, "/assets/tpl/*.html"))
2020-11-05 13:37:51 -05:00
s.server.LoadHTMLGlob(filepath.Join(dir, "/assets/tpl/*.html"))
2020-11-09 14:26:34 -05:00
s.server.Use(sessions.Sessions("authsession", memstore.NewStore([]byte("secret")))) // TODO: change key?
2020-11-05 13:37:51 -05:00
// Serve static files
s.server.Static("/css", filepath.Join(dir, "/assets/css"))
s.server.Static("/js", filepath.Join(dir, "/assets/js"))
s.server.Static("/img", filepath.Join(dir, "/assets/img"))
s.server.Static("/fonts", filepath.Join(dir, "/assets/fonts"))
// Setup all routes
SetupRoutes(s)
2021-02-08 16:56:02 -05:00
logrus.Infof("Setup of service completed!")
2020-11-05 13:37:51 -05:00
return nil
}
func (s *Server) Run() {
// Start ldap group watcher
2020-11-10 03:31:02 -05:00
if !s.ldapDisabled {
go func(s *Server) {
for {
time.Sleep(CacheRefreshDuration)
if err := s.ldapCacheUpdater.Update(true, true); err != nil {
2021-02-08 16:56:02 -05:00
logrus.Warnf("Failed to update ldap group cache: %v", err)
2020-11-10 03:31:02 -05:00
}
2021-02-08 16:56:02 -05:00
logrus.Debugf("Refreshed LDAP permissions!")
2020-11-05 13:37:51 -05:00
}
2020-11-10 03:31:02 -05:00
}(s)
}
2020-11-05 13:37:51 -05:00
if !s.ldapDisabled && s.config.Core.SyncLdapStatus {
go func(s *Server) {
for {
time.Sleep(CacheRefreshDuration)
if err := s.SyncLdapAttributesWithWireGuard(); err != nil {
2021-02-08 16:56:02 -05:00
logrus.Warnf("Failed to synchronize ldap attributes: %v", err)
}
2021-02-08 16:56:02 -05:00
logrus.Debugf("Synced LDAP attributes!")
}
}(s)
}
2020-11-05 13:37:51 -05:00
// Run web service
err := s.server.Run(s.config.Core.ListeningAddress)
if err != nil {
2021-02-08 16:56:02 -05:00
logrus.Errorf("Failed to listen and serve on %s: %v", s.config.Core.ListeningAddress, err)
2020-11-05 13:37:51 -05:00
}
}
func (s *Server) getExecutableDirectory() string {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
2021-02-08 16:56:02 -05:00
logrus.Errorf("Failed to get executable directory: %v", err)
2020-11-05 13:37:51 -05:00
}
if _, err := os.Stat(filepath.Join(dir, "assets")); os.IsNotExist(err) {
return "." // assets directory not found -> we are developing in goland =)
}
return dir
}
func (s *Server) getSessionData(c *gin.Context) SessionData {
session := sessions.Default(c)
rawSessionData := session.Get(SessionIdentifier)
var sessionData SessionData
if rawSessionData != nil {
sessionData = rawSessionData.(SessionData)
} else {
sessionData = SessionData{
2020-11-09 14:26:34 -05:00
SortedBy: "mail",
2020-11-05 13:37:51 -05:00
SortDirection: "asc",
Email: "",
2020-11-05 13:37:51 -05:00
Firstname: "",
Lastname: "",
IsAdmin: false,
LoggedIn: false,
}
session.Set(SessionIdentifier, sessionData)
if err := session.Save(); err != nil {
2021-02-08 16:56:02 -05:00
logrus.Errorf("Failed to store session: %v", err)
2020-11-05 13:37:51 -05:00
}
}
return sessionData
}
2020-11-10 16:23:05 -05:00
func (s *Server) getFlashes(c *gin.Context) []FlashData {
session := sessions.Default(c)
flashes := session.Flashes()
if err := session.Save(); err != nil {
2021-02-08 16:56:02 -05:00
logrus.Errorf("Failed to store session after setting flash: %v", err)
2020-11-10 16:23:05 -05:00
}
flashData := make([]FlashData, len(flashes))
for i := range flashes {
flashData[i] = flashes[i].(FlashData)
2020-11-05 13:37:51 -05:00
}
2020-11-10 16:23:05 -05:00
return flashData
2020-11-05 13:37:51 -05:00
}
func (s *Server) updateSessionData(c *gin.Context, data SessionData) error {
session := sessions.Default(c)
session.Set(SessionIdentifier, data)
if err := session.Save(); err != nil {
2021-02-08 16:56:02 -05:00
logrus.Errorf("Failed to store session: %v", err)
2020-11-05 13:37:51 -05:00
return err
}
return nil
}
func (s *Server) destroySessionData(c *gin.Context) error {
session := sessions.Default(c)
session.Delete(SessionIdentifier)
if err := session.Save(); err != nil {
2021-02-08 16:56:02 -05:00
logrus.Errorf("Failed to destroy session: %v", err)
2020-11-05 13:37:51 -05:00
return err
}
return nil
}
func (s *Server) getStaticData() StaticData {
return StaticData{
WebsiteTitle: s.config.Core.Title,
2020-11-10 03:31:02 -05:00
WebsiteLogo: "/img/header-logo.png",
CompanyName: s.config.Core.CompanyName,
LdapDisabled: s.ldapDisabled,
Year: time.Now().Year(),
2020-11-05 13:37:51 -05:00
}
}
2020-11-10 16:23:05 -05:00
func (s *Server) setFlashMessage(c *gin.Context, message, typ string) {
session := sessions.Default(c)
session.AddFlash(FlashData{
Message: message,
Type: typ,
})
if err := session.Save(); err != nil {
2021-02-08 16:56:02 -05:00
logrus.Errorf("Failed to store session after setting flash: %v", err)
2020-11-10 16:23:05 -05:00
}
2020-11-05 13:37:51 -05:00
}
func (s SessionData) GetSortIcon(field string) string {
if s.SortedBy != field {
return "fa-sort"
}
if s.SortDirection == "asc" {
return "fa-sort-alpha-down"
} else {
return "fa-sort-alpha-up"
}
}