website/pkg/controller/chat/chat.go
Noah Petherbridge 7d17dce4d4 Shy Accounts
* Users with private profiles or no public photo at all are considered
  to be Shy Accounts and are isolated from the non-shy profiles.
* Restrictions include:
  * Site Gallery shows only them + their friends' photos.
  * User Galleries: must be a friend or had private photos granted to
    see a user's gallery page.
  * DMs: can not initiate a DM to a non-shy member (other shy members
    OK).
2023-02-13 22:19:18 -08:00

116 lines
3.2 KiB
Go

package chat
import (
"fmt"
"net/http"
"strings"
"time"
"code.nonshy.com/nonshy/website/pkg/config"
"code.nonshy.com/nonshy/website/pkg/models"
"code.nonshy.com/nonshy/website/pkg/photo"
"code.nonshy.com/nonshy/website/pkg/session"
"code.nonshy.com/nonshy/website/pkg/templates"
"github.com/golang-jwt/jwt/v4"
)
// JWT claims.
type Claims struct {
// Custom claims.
IsAdmin bool `json:"op"`
Avatar string `json:"img"`
ProfileURL string `json:"url"`
// Standard claims. Notes:
// subject = username
jwt.RegisteredClaims
}
// Landing page for chat rooms.
func Landing() http.HandlerFunc {
tmpl := templates.Must("chat.html")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Get the current user.
currentUser, err := session.CurrentUser(r)
if err != nil {
session.FlashError(w, r, "Couldn't get current user: %s", err)
templates.Redirect(w, "/")
return
}
// Are they logging into the chat room?
var (
intent = r.FormValue("intent")
isShy = currentUser.IsShy()
)
if intent == "join" {
// If we are shy, block chat for now.
if isShy {
session.FlashError(w, r,
"You have a Shy Account and are not allowed in the chat room at this time where our non-shy members may "+
"be on camera.",
)
templates.Redirect(w, "/chat")
return
}
// Get our Chat JWT secret.
var (
secret = []byte(config.Current.BareRTC.JWTSecret)
chatURL = config.Current.BareRTC.URL
)
if len(secret) == 0 || chatURL == "" {
session.FlashError(w, r, "Couldn't sign you into the chat: JWT secret key or chat URL not configured!")
templates.Redirect(w, r.URL.Path)
return
}
// Avatar URL - masked if non-public.
avatar := photo.URLPath(currentUser.ProfilePhoto.CroppedFilename)
switch currentUser.ProfilePhoto.Visibility {
case models.PhotoPrivate:
avatar = "/static/img/shy-private.png"
case models.PhotoFriends:
avatar = "/static/img/shy-friends.png"
}
// Create the JWT claims.
claims := Claims{
IsAdmin: currentUser.IsAdmin,
Avatar: avatar,
ProfileURL: "/u/" + currentUser.Username,
RegisteredClaims: jwt.RegisteredClaims{
// TODO: ExpiresAt 60 minutes to work around chat server reliability,
// should be shorter like 5 minutes.
ExpiresAt: jwt.NewNumericDate(time.Now().Add(60 * time.Minute)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
Issuer: config.Title,
Subject: currentUser.Username,
ID: fmt.Sprintf("%d", currentUser.ID),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString(secret)
if err != nil {
session.FlashError(w, r, "Couldn't sign you into the chat: %s", err)
templates.Redirect(w, r.URL.Path)
return
}
// Redirect them to the chat room.
templates.Redirect(w, strings.TrimSuffix(chatURL, "/")+"/?jwt="+ss)
return
}
var vars = map[string]interface{}{
"ChatAPI": strings.TrimSuffix(config.Current.BareRTC.URL, "/") + "/api/statistics",
"IsShyUser": isShy,
}
if err := tmpl.Execute(w, r, vars); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
}