162 lines
5.6 KiB
Go
162 lines
5.6 KiB
Go
/*
|
|
* Amsterdam Web Communities System
|
|
* Copyright (c) 2025 Erbosoft Metaverse Design Solutions, All Rights Reserved
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*/
|
|
|
|
// Package ui holds the support for the Amsterdam user interface, wrapping Echo and Jet templates.
|
|
package ui
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"git.erbosoft.com/amy/amsterdam/config"
|
|
"git.erbosoft.com/amy/amsterdam/database"
|
|
"github.com/labstack/echo/v4"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// IPBanTest is middleware that handles the IP banning.
|
|
func IPBanTest(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
// Check IP banning.
|
|
banmsg, banerr := database.AmTestIPBan(c.Request().Context(), c.RealIP())
|
|
if banerr != nil {
|
|
c.Logger().Warnf("address %s could not be tested: %v", c.RealIP(), banerr)
|
|
// but let the request pass anyway
|
|
} else if banmsg != "" {
|
|
amctxt := AmContextFromEchoContext(c)
|
|
return AmSendPageData(c, amctxt, "ipban", banmsg)
|
|
}
|
|
return next(c)
|
|
}
|
|
}
|
|
|
|
// CookieLoginTest is middleware that handles cookie logins.
|
|
func CookieLoginTest(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
amctxt := AmContextFromEchoContext(c)
|
|
// Check for cookie login.
|
|
if amctxt.CurrentUser().IsAnon {
|
|
cookie, err := c.Cookie(config.GlobalConfig.Site.LoginCookieName)
|
|
if err == nil {
|
|
var user *database.User
|
|
user, err = database.AmAuthenticateUserByToken(c.Request().Context(), cookie.Value, c.RealIP())
|
|
if err == nil {
|
|
// log the user in and rotate login cookie
|
|
amctxt.ReplaceUser(user)
|
|
var newToken string
|
|
if newToken, err = user.NewAuthToken(c.Request().Context()); err == nil {
|
|
amctxt.SetLoginCookie(newToken)
|
|
} else {
|
|
log.Warnf("unable to rotate login cookie: %v", err)
|
|
}
|
|
if !user.VerifyEMail {
|
|
// bounce to E-mail verification before we go anywhere
|
|
return AmSendPageData(c, amctxt, "redirect",
|
|
"/verify?tgt="+url.QueryEscape(c.Request().URL.Path))
|
|
}
|
|
} else {
|
|
log.Errorf("login cookie bogus, do not use: %v", err)
|
|
amctxt.ClearLoginCookie()
|
|
}
|
|
}
|
|
}
|
|
return next(c)
|
|
}
|
|
}
|
|
|
|
// SetCommunity is middleware that sets the community context based on the URL.
|
|
func SetCommunity(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
ctxt := AmContextFromEchoContext(c)
|
|
err := ctxt.SetCommunityContext(ctxt.URLParam("cid"))
|
|
if err != nil {
|
|
return AmSendPageData(c, ctxt, "error", echo.NewHTTPError(http.StatusNotFound).SetInternal(err))
|
|
}
|
|
var b strings.Builder
|
|
b.WriteString("/comm/")
|
|
b.WriteString(ctxt.CurrentCommunity().Alias)
|
|
ctxt.SetScratch("CommunityLink", b.String())
|
|
ctxt.SetLeftMenu("community")
|
|
return next(c)
|
|
}
|
|
}
|
|
|
|
// ValidateConference is middleware that validates the user has access to the community's conference facility.
|
|
func ValidateConference(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
ctxt := AmContextFromEchoContext(c)
|
|
comm := ctxt.CurrentCommunity() // set by middleware
|
|
b, err := database.AmTestService(c.Request().Context(), comm, "Conference")
|
|
if err != nil {
|
|
return AmSendPageData(c, ctxt, "error", err)
|
|
}
|
|
if !b {
|
|
return AmSendPageData(c, ctxt, "error", echo.NewHTTPError(http.StatusNotFound, "this community does not use conferencing services"))
|
|
}
|
|
if comm.MembersOnly && !ctxt.IsMember() && !ctxt.TestPermission("Community.NoJoinRequired") {
|
|
return AmSendPageData(c, ctxt, "error", echo.NewHTTPError(http.StatusForbidden, "you are not a member of this community"))
|
|
}
|
|
if !comm.TestPermission("Community.Read", ctxt.EffectiveLevel()) {
|
|
return AmSendPageData(c, ctxt, "error", echo.NewHTTPError(http.StatusForbidden, "you are not authorized access to conferences"))
|
|
}
|
|
return next(c)
|
|
}
|
|
}
|
|
|
|
// SetConference is middleware that sets the conference context based on the URL.
|
|
func SetConference(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
ctxt := AmContextFromEchoContext(c)
|
|
conf, err := database.AmGetConferenceByAlias(ctxt.Ctx(), ctxt.CurrentCommunity().Id, ctxt.URLParam("confid"))
|
|
if err != nil {
|
|
return AmSendPageData(c, ctxt, "error", err)
|
|
}
|
|
m, lvl, err := conf.Membership(ctxt.Ctx(), ctxt.CurrentUser())
|
|
if err != nil {
|
|
return AmSendPageData(c, ctxt, "error", err)
|
|
}
|
|
myLevel := ctxt.EffectiveLevel()
|
|
if m && lvl > myLevel {
|
|
myLevel = lvl
|
|
}
|
|
ctxt.SetScratch("currentConference", conf)
|
|
ctxt.SetScratch("currentAlias", ctxt.URLParam("confid"))
|
|
ctxt.SetScratch("levelInConference", myLevel)
|
|
var b strings.Builder
|
|
b.WriteString(ctxt.GetScratch("CommunityLink").(string))
|
|
b.WriteString("/conf/")
|
|
b.WriteString(ctxt.URLParam("confid"))
|
|
ctxt.SetScratch("ConferenceLink", b.String())
|
|
return next(c)
|
|
}
|
|
}
|
|
|
|
// SetTopic is middleware that sets the topic context based on the URL.
|
|
func SetTopic(next echo.HandlerFunc) echo.HandlerFunc {
|
|
return func(c echo.Context) error {
|
|
ctxt := AmContextFromEchoContext(c)
|
|
conf := ctxt.GetScratch("currentConference").(*database.Conference)
|
|
|
|
var topic *database.Topic = nil
|
|
if rawTopic, err := strconv.ParseInt(ctxt.URLParam("topic"), 10, 16); err == nil {
|
|
topic, err = database.AmGetTopicByNumber(ctxt.Ctx(), conf, int16(rawTopic))
|
|
}
|
|
if topic == nil {
|
|
return AmSendPageData(c, ctxt, "error", echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("topic not found: %s", ctxt.URLParam("topic"))))
|
|
}
|
|
ctxt.SetScratch("currentTopic", topic)
|
|
return next(c)
|
|
}
|
|
}
|