User repository (#3795)

* It builds with the new user repository

* fix(test): fix broken test

* fix(api): fix registration endpoint that was broken after the change

* fix(test): update test to reflect new user repository

* fix: use interface type instead of concrete type

* fix: restore commented out code
This commit is contained in:
Gabe Kangas
2024-07-01 18:58:50 -07:00
committed by GitHub
parent 76be78d1b8
commit 2ccd3aad87
41 changed files with 1175 additions and 1153 deletions

View File

@@ -13,13 +13,14 @@ import (
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
// ExternalUpdateMessageVisibility updates an array of message IDs to have the same visiblity.
func ExternalUpdateMessageVisibility(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
func ExternalUpdateMessageVisibility(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
UpdateMessageVisibility(w, r)
}
@@ -130,8 +131,10 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
return
}
userRepository := userrepository.Get()
// Disable/enable the user
if err := user.SetEnabled(request.UserID, request.Enabled); err != nil {
if err := userRepository.SetEnabled(request.UserID, request.Enabled); err != nil {
log.Errorln("error changing user enabled status", err)
controllers.WriteSimpleResponse(w, false, err.Error())
return
@@ -162,7 +165,7 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
}
chat.DisconnectClients(clients)
disconnectedUser := user.GetUserByID(request.UserID)
disconnectedUser := userRepository.GetUserByID(request.UserID)
_ = chat.SendSystemAction(fmt.Sprintf("**%s** has been removed from chat.", disconnectedUser.DisplayName), true)
localIP4Address := "127.0.0.1"
@@ -187,7 +190,9 @@ func UpdateUserEnabled(w http.ResponseWriter, r *http.Request) {
func GetDisabledUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
users := user.GetDisabledUsers()
userRepository := userrepository.Get()
users := userRepository.GetDisabledUsers()
controllers.WriteResponse(w, users)
}
@@ -198,7 +203,7 @@ func UpdateUserModerator(w http.ResponseWriter, r *http.Request) {
IsModerator bool `json:"isModerator"`
}
if r.Method != controllers.POST {
if r.Method != http.MethodPost {
controllers.WriteSimpleResponse(w, false, r.Method+" not supported")
return
}
@@ -211,8 +216,10 @@ func UpdateUserModerator(w http.ResponseWriter, r *http.Request) {
return
}
userRepository := userrepository.Get()
// Update the user object with new moderation access.
if err := user.SetModerator(req.UserID, req.IsModerator); err != nil {
if err := userRepository.SetModerator(req.UserID, req.IsModerator); err != nil {
controllers.WriteSimpleResponse(w, false, err.Error())
return
}
@@ -229,7 +236,9 @@ func UpdateUserModerator(w http.ResponseWriter, r *http.Request) {
func GetModerators(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
users := user.GetModeratorUsers()
userRepository := userrepository.Get()
users := userRepository.GetModeratorUsers()
controllers.WriteResponse(w, users)
}
@@ -242,7 +251,7 @@ func GetChatMessages(w http.ResponseWriter, r *http.Request) {
}
// SendSystemMessage will send an official "SYSTEM" message to chat on behalf of your server.
func SendSystemMessage(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
func SendSystemMessage(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var message events.SystemMessageEvent
@@ -259,7 +268,7 @@ func SendSystemMessage(integration user.ExternalAPIUser, w http.ResponseWriter,
}
// SendSystemMessageToConnectedClient will handle incoming requests to send a single message to a single connected client by ID.
func SendSystemMessageToConnectedClient(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
func SendSystemMessageToConnectedClient(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
clientIDText, err := utils.GetURLParam(r, "clientId")
if err != nil {
@@ -284,13 +293,13 @@ func SendSystemMessageToConnectedClient(integration user.ExternalAPIUser, w http
}
// SendUserMessage will send a message to chat on behalf of a user. *Depreciated*.
func SendUserMessage(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
func SendUserMessage(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
controllers.BadRequestHandler(w, errors.New("no longer supported. see /api/integrations/chat/send"))
}
// SendIntegrationChatMessage will send a chat message on behalf of an external chat integration.
func SendIntegrationChatMessage(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
func SendIntegrationChatMessage(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
name := integration.DisplayName
@@ -314,7 +323,7 @@ func SendIntegrationChatMessage(integration user.ExternalAPIUser, w http.Respons
return
}
event.User = &user.User{
event.User = &models.User{
ID: integration.ID,
DisplayName: name,
DisplayColor: integration.DisplayColor,
@@ -333,7 +342,7 @@ func SendIntegrationChatMessage(integration user.ExternalAPIUser, w http.Respons
}
// SendChatAction will send a generic chat action.
func SendChatAction(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
func SendChatAction(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var message events.SystemActionEvent

View File

@@ -15,7 +15,6 @@ import (
"github.com/owncast/owncast/controllers"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/core/webhooks"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/utils"
@@ -83,7 +82,7 @@ func SetStreamTitle(w http.ResponseWriter, r *http.Request) {
}
// ExternalSetStreamTitle will change the stream title on behalf of an external integration API request.
func ExternalSetStreamTitle(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
func ExternalSetStreamTitle(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
SetStreamTitle(w, r)
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/owncast/owncast/controllers"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/models"
)
// GetConnectedChatClients returns currently connected clients.
@@ -20,6 +20,6 @@ func GetConnectedChatClients(w http.ResponseWriter, r *http.Request) {
}
// ExternalGetConnectedChatClients returns currently connected clients.
func ExternalGetConnectedChatClients(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
func ExternalGetConnectedChatClients(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
GetConnectedChatClients(w, r)
}

View File

@@ -8,7 +8,8 @@ import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/controllers"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/utils"
)
@@ -30,8 +31,10 @@ func CreateExternalAPIUser(w http.ResponseWriter, r *http.Request) {
return
}
userRepository := userrepository.Get()
// Verify all the scopes provided are valid
if !user.HasValidScopes(request.Scopes) {
if !userRepository.HasValidScopes(request.Scopes) {
controllers.BadRequestHandler(w, errors.New("one or more invalid scopes provided"))
return
}
@@ -44,13 +47,13 @@ func CreateExternalAPIUser(w http.ResponseWriter, r *http.Request) {
color := utils.GenerateRandomDisplayColor(config.MaxUserColor)
if err := user.InsertExternalAPIUser(token, request.Name, color, request.Scopes); err != nil {
if err := userRepository.InsertExternalAPIUser(token, request.Name, color, request.Scopes); err != nil {
controllers.InternalErrorHandler(w, err)
return
}
w.Header().Set("Content-Type", "application/json")
controllers.WriteResponse(w, user.ExternalAPIUser{
controllers.WriteResponse(w, models.ExternalAPIUser{
AccessToken: token,
DisplayName: request.Name,
DisplayColor: color,
@@ -64,7 +67,9 @@ func CreateExternalAPIUser(w http.ResponseWriter, r *http.Request) {
func GetExternalAPIUsers(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
tokens, err := user.GetExternalAPIUser()
userRepository := userrepository.Get()
tokens, err := userRepository.GetExternalAPIUser()
if err != nil {
controllers.InternalErrorHandler(w, err)
return
@@ -93,7 +98,9 @@ func DeleteExternalAPIUser(w http.ResponseWriter, r *http.Request) {
return
}
if err := user.DeleteExternalAPIUser(request.Token); err != nil {
userRepository := userrepository.Get()
if err := userRepository.DeleteExternalAPIUser(request.Token); err != nil {
controllers.InternalErrorHandler(w, err)
return
}

View File

@@ -8,7 +8,6 @@ import (
"github.com/owncast/owncast/controllers"
"github.com/owncast/owncast/core"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/metrics"
"github.com/owncast/owncast/models"
log "github.com/sirupsen/logrus"
@@ -50,6 +49,6 @@ func GetActiveViewers(w http.ResponseWriter, r *http.Request) {
}
// ExternalGetActiveViewers returns currently connected clients.
func ExternalGetActiveViewers(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
func ExternalGetActiveViewers(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
GetConnectedChatClients(w, r)
}

View File

@@ -6,17 +6,17 @@ import (
"net/http"
"github.com/owncast/owncast/activitypub"
"github.com/owncast/owncast/auth"
fediverseauth "github.com/owncast/owncast/auth/fediverse"
"github.com/owncast/owncast/controllers"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/userrepository"
log "github.com/sirupsen/logrus"
)
// RegisterFediverseOTPRequest registers a new OTP request for the given access token.
func RegisterFediverseOTPRequest(u user.User, w http.ResponseWriter, r *http.Request) {
func RegisterFediverseOTPRequest(u models.User, w http.ResponseWriter, r *http.Request) {
type request struct {
FediverseAccount string `json:"account"`
}
@@ -67,14 +67,16 @@ func VerifyFediverseOTPRequest(w http.ResponseWriter, r *http.Request) {
return
}
userRepository := userrepository.Get()
// Check if a user with this auth already exists, if so, log them in.
if u := auth.GetUserByAuth(authRegistration.Account, auth.Fediverse); u != nil {
if u := userRepository.GetUserByAuth(authRegistration.Account, models.Fediverse); u != nil {
// Handle existing auth.
log.Debugln("user with provided fedvierse identity already exists, logging them in")
// Update the current user's access token to point to the existing user id.
userID := u.ID
if err := user.SetAccessTokenToOwner(accessToken, userID); err != nil {
if err := userRepository.SetAccessTokenToOwner(accessToken, userID); err != nil {
controllers.WriteSimpleResponse(w, false, err.Error())
return
}
@@ -93,14 +95,14 @@ func VerifyFediverseOTPRequest(w http.ResponseWriter, r *http.Request) {
// Otherwise, save this as new auth.
log.Debug("fediverse account does not already exist, saving it as a new one for the current user")
if err := auth.AddAuth(authRegistration.UserID, authRegistration.Account, auth.Fediverse); err != nil {
if err := userRepository.AddAuth(authRegistration.UserID, authRegistration.Account, models.Fediverse); err != nil {
controllers.WriteSimpleResponse(w, false, err.Error())
return
}
// Update the current user's authenticated flag so we can show it in
// the chat UI.
if err := user.SetUserAsAuthenticated(authRegistration.UserID); err != nil {
if err := userRepository.SetUserAsAuthenticated(authRegistration.UserID); err != nil {
log.Errorln(err)
}

View File

@@ -6,16 +6,16 @@ import (
"io"
"net/http"
"github.com/owncast/owncast/auth"
ia "github.com/owncast/owncast/auth/indieauth"
"github.com/owncast/owncast/controllers"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/userrepository"
log "github.com/sirupsen/logrus"
)
// StartAuthFlow will begin the IndieAuth flow for the current user.
func StartAuthFlow(u user.User, w http.ResponseWriter, r *http.Request) {
func StartAuthFlow(u models.User, w http.ResponseWriter, r *http.Request) {
type request struct {
AuthHost string `json:"authHost"`
}
@@ -63,15 +63,17 @@ func HandleRedirect(w http.ResponseWriter, r *http.Request) {
return
}
userRepository := userrepository.Get()
// Check if a user with this auth already exists, if so, log them in.
if u := auth.GetUserByAuth(response.Me, auth.IndieAuth); u != nil {
if u := userRepository.GetUserByAuth(response.Me, models.IndieAuth); u != nil {
// Handle existing auth.
log.Debugln("user with provided indieauth already exists, logging them in")
// Update the current user's access token to point to the existing user id.
accessToken := request.CurrentAccessToken
userID := u.ID
if err := user.SetAccessTokenToOwner(accessToken, userID); err != nil {
if err := userRepository.SetAccessTokenToOwner(accessToken, userID); err != nil {
controllers.WriteSimpleResponse(w, false, err.Error())
return
}
@@ -90,14 +92,14 @@ func HandleRedirect(w http.ResponseWriter, r *http.Request) {
// Otherwise, save this as new auth.
log.Debug("indieauth token does not already exist, saving it as a new one for the current user")
if err := auth.AddAuth(request.UserID, response.Me, auth.IndieAuth); err != nil {
if err := userRepository.AddAuth(request.UserID, response.Me, models.IndieAuth); err != nil {
controllers.WriteSimpleResponse(w, false, err.Error())
return
}
// Update the current user's authenticated flag so we can show it in
// the chat UI.
if err := user.SetUserAsAuthenticated(request.UserID); err != nil {
if err := userRepository.SetUserAsAuthenticated(request.UserID); err != nil {
log.Errorln(err)
}

View File

@@ -6,20 +6,22 @@ import (
"github.com/owncast/owncast/config"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/core/data"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/userrepository"
"github.com/owncast/owncast/router/middleware"
"github.com/owncast/owncast/utils"
log "github.com/sirupsen/logrus"
)
// ExternalGetChatMessages gets all of the chat messages.
func ExternalGetChatMessages(integration user.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
func ExternalGetChatMessages(integration models.ExternalAPIUser, w http.ResponseWriter, r *http.Request) {
middleware.EnableCors(w)
getChatMessages(w, r)
}
// GetChatMessages gets all of the chat messages.
func GetChatMessages(u user.User, w http.ResponseWriter, r *http.Request) {
func GetChatMessages(u models.User, w http.ResponseWriter, r *http.Request) {
middleware.EnableCors(w)
getChatMessages(w, r)
}
@@ -46,7 +48,9 @@ func getChatMessages(w http.ResponseWriter, r *http.Request) {
func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
middleware.EnableCors(w)
if r.Method == "OPTIONS" {
userRepository := userrepository.Get()
if r.Method == http.MethodOptions {
// All OPTIONS requests should have a wildcard CORS header.
w.Header().Set("Access-Control-Allow-Origin", "*")
w.WriteHeader(http.StatusNoContent)
@@ -75,12 +79,16 @@ func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
// this is fine. register a new user anyway.
}
if request.DisplayName == "" {
request.DisplayName = r.Header.Get("X-Forwarded-User")
proposedNewDisplayName := r.Header.Get("X-Forwarded-User")
if proposedNewDisplayName == "" {
proposedNewDisplayName = request.DisplayName
}
if proposedNewDisplayName == "" {
proposedNewDisplayName = generateDisplayName()
}
proposedNewDisplayName := utils.MakeSafeStringOfLength(request.DisplayName, config.MaxChatDisplayNameLength)
newUser, accessToken, err := user.CreateAnonymousUser(proposedNewDisplayName)
proposedNewDisplayName = utils.MakeSafeStringOfLength(proposedNewDisplayName, config.MaxChatDisplayNameLength)
newUser, accessToken, err := userRepository.CreateAnonymousUser(proposedNewDisplayName)
if err != nil {
WriteSimpleResponse(w, false, err.Error())
return
@@ -97,3 +105,15 @@ func RegisterAnonymousChatUser(w http.ResponseWriter, r *http.Request) {
WriteResponse(w, response)
}
func generateDisplayName() string {
suggestedUsernamesList := data.GetSuggestedUsernamesList()
minSuggestedUsernamePoolLength := 10
if len(suggestedUsernamesList) >= minSuggestedUsernamePoolLength {
index := utils.RandomIndex(len(suggestedUsernamesList))
return suggestedUsernamesList[index]
} else {
return utils.GeneratePhrase()
}
}

View File

@@ -9,7 +9,8 @@ import (
"github.com/owncast/owncast/controllers"
"github.com/owncast/owncast/core/chat"
"github.com/owncast/owncast/core/chat/events"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/persistence/userrepository"
log "github.com/sirupsen/logrus"
)
@@ -24,7 +25,7 @@ func GetUserDetails(w http.ResponseWriter, r *http.Request) {
}
type response struct {
User *user.User `json:"user"`
User *models.User `json:"user"`
ConnectedClients []connectedClient `json:"connectedClients"`
Messages []events.UserMessageEvent `json:"messages"`
}
@@ -32,7 +33,9 @@ func GetUserDetails(w http.ResponseWriter, r *http.Request) {
pathComponents := strings.Split(r.URL.Path, "/")
uid := pathComponents[len(pathComponents)-1]
u := user.GetUserByID(uid)
userRepository := userrepository.Get()
u := userRepository.GetUserByID(uid)
if u == nil {
w.WriteHeader(http.StatusNotFound)

View File

@@ -4,7 +4,7 @@ import (
"encoding/json"
"net/http"
"github.com/owncast/owncast/core/user"
"github.com/owncast/owncast/models"
"github.com/owncast/owncast/notifications"
"github.com/owncast/owncast/utils"
@@ -14,8 +14,8 @@ import (
// RegisterForLiveNotifications will register a channel + destination to be
// notified when a stream goes live.
func RegisterForLiveNotifications(u user.User, w http.ResponseWriter, r *http.Request) {
if r.Method != POST {
func RegisterForLiveNotifications(u models.User, w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
WriteSimpleResponse(w, false, r.Method+" not supported")
return
}