pushbits/internal/api/application.go
eikendev d7721e827b
Add option to enforce strict compatibility
Some plugins check for token length. Since PushBits uses longer tokens by default for better security, these plugins are
incompatible. With this patch, users can decide if they want an application to have a short token, so that said plugins
can talk to PushBits again.
2021-02-08 00:34:33 +01:00

213 lines
5.5 KiB
Go

package api
import (
"errors"
"log"
"net/http"
"github.com/pushbits/server/internal/authentication"
"github.com/pushbits/server/internal/model"
"github.com/gin-gonic/gin"
)
// ApplicationHandler holds information for processing requests about applications.
type ApplicationHandler struct {
DB Database
DP Dispatcher
}
func (h *ApplicationHandler) applicationExists(token string) bool {
application, _ := h.DB.GetApplicationByToken(token)
return application != nil
}
func (h *ApplicationHandler) generateToken(compat bool) string {
return authentication.GenerateNotExistingToken(authentication.GenerateApplicationToken, compat, h.applicationExists)
}
func (h *ApplicationHandler) registerApplication(ctx *gin.Context, a *model.Application, u *model.User) error {
log.Printf("Registering application %s.\n", a.Name)
channelID, err := h.DP.RegisterApplication(a.ID, a.Name, a.Token, u.MatrixID)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
return err
}
a.MatrixID = channelID
h.DB.UpdateApplication(a)
return nil
}
func (h *ApplicationHandler) createApplication(ctx *gin.Context, u *model.User, name string, compat bool) (*model.Application, error) {
log.Printf("Creating application %s.\n", name)
application := model.Application{}
application.Name = name
application.Token = h.generateToken(compat)
application.UserID = u.ID
err := h.DB.CreateApplication(&application)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
return nil, err
}
if err := h.registerApplication(ctx, &application, u); err != nil {
err := h.DB.DeleteApplication(&application)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
log.Printf("Cannot delete application with ID %d.\n", application.ID)
}
return nil, err
}
return &application, nil
}
func (h *ApplicationHandler) deleteApplication(ctx *gin.Context, a *model.Application, u *model.User) error {
log.Printf("Deleting application %s (ID %d).\n", a.Name, a.ID)
err := h.DP.DeregisterApplication(a, u)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
return err
}
err = h.DB.DeleteApplication(a)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
return err
}
return nil
}
func (h *ApplicationHandler) updateApplication(ctx *gin.Context, a *model.Application, u *model.User, updateApplication *model.UpdateApplication) error {
log.Printf("Updating application %s (ID %d).\n", a.Name, a.ID)
if updateApplication.Name != nil {
log.Printf("Updating application name to '%s'.", *updateApplication.Name)
a.Name = *updateApplication.Name
}
if updateApplication.RefreshToken != nil && (*updateApplication.RefreshToken) {
log.Print("Updating application token.")
compat := updateApplication.StrictCompatibility != nil && (*updateApplication.StrictCompatibility)
a.Token = h.generateToken(compat)
}
err := h.DB.UpdateApplication(a)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
return err
}
err = h.DP.UpdateApplication(a, u)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
return err
}
return nil
}
// CreateApplication creates an application.
func (h *ApplicationHandler) CreateApplication(ctx *gin.Context) {
var createApplication model.CreateApplication
if err := ctx.Bind(&createApplication); err != nil {
return
}
user := authentication.GetUser(ctx)
if user == nil {
return
}
application, err := h.createApplication(ctx, user, createApplication.Name, createApplication.StrictCompatibility)
if err != nil {
return
}
ctx.JSON(http.StatusOK, &application)
}
// GetApplications returns all applications of the current user.
func (h *ApplicationHandler) GetApplications(ctx *gin.Context) {
user := authentication.GetUser(ctx)
if user == nil {
return
}
applications, err := h.DB.GetApplications(user)
if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success {
return
}
ctx.JSON(http.StatusOK, &applications)
}
// GetApplication returns the application with the specified ID.
func (h *ApplicationHandler) GetApplication(ctx *gin.Context) {
application, err := getApplication(ctx, h.DB)
if err != nil {
return
}
user := authentication.GetUser(ctx)
if user == nil {
return
}
if user.ID != application.UserID {
err := errors.New("application belongs to another user")
ctx.AbortWithError(http.StatusForbidden, err)
return
}
ctx.JSON(http.StatusOK, &application)
}
// DeleteApplication deletes an application with a certain ID.
func (h *ApplicationHandler) DeleteApplication(ctx *gin.Context) {
application, err := getApplication(ctx, h.DB)
if err != nil {
return
}
if !isCurrentUser(ctx, application.UserID) {
return
}
if err := h.deleteApplication(ctx, application, authentication.GetUser(ctx)); err != nil {
return
}
ctx.JSON(http.StatusOK, gin.H{})
}
// UpdateApplication updates an application with a certain ID.
func (h *ApplicationHandler) UpdateApplication(ctx *gin.Context) {
application, err := getApplication(ctx, h.DB)
if err != nil {
return
}
if !isCurrentUser(ctx, application.UserID) {
return
}
var updateApplication model.UpdateApplication
if err := ctx.Bind(&updateApplication); err != nil {
return
}
user := authentication.GetUser(ctx)
if user == nil {
return
}
if err := h.updateApplication(ctx, application, user, &updateApplication); err != nil {
return
}
ctx.JSON(http.StatusOK, gin.H{})
}