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.", 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.", 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.", 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).", 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, updateApplication *model.UpdateApplication) error { log.Printf("Updating application %s (ID %d).", 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) 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 } if err := h.updateApplication(ctx, application, &updateApplication); err != nil { return } ctx.JSON(http.StatusOK, gin.H{}) }