diff --git a/api/application.go b/api/application.go index 98037bd..7b03ec7 100644 --- a/api/application.go +++ b/api/application.go @@ -21,7 +21,7 @@ type ApplicationDatabase interface { // The ApplicationDispatcher interface for relaying notifications. type ApplicationDispatcher interface { RegisterApplication(name, user string) (string, error) - DeregisterApplication(matrixID string) error + DeregisterApplication(a *model.Application) error } // ApplicationHandler holds information for processing requests about applications. @@ -35,7 +35,7 @@ func (h *ApplicationHandler) applicationExists(token string) bool { return application != nil } -// CreateApplication creates a user. +// CreateApplication creates an application. func (h *ApplicationHandler) CreateApplication(ctx *gin.Context) { var createApplication model.CreateApplication @@ -66,7 +66,7 @@ func (h *ApplicationHandler) CreateApplication(ctx *gin.Context) { ctx.JSON(http.StatusOK, &application) } -// DeleteApplication deletes a user with a certain ID. +// DeleteApplication deletes an application with a certain ID. func (h *ApplicationHandler) DeleteApplication(ctx *gin.Context) { var deleteApplication model.DeleteApplication @@ -76,13 +76,13 @@ func (h *ApplicationHandler) DeleteApplication(ctx *gin.Context) { application, err := h.DB.GetApplicationByID(deleteApplication.ID) - log.Printf("Deleting application %s.\n", application.Name) - if success := successOrAbort(ctx, http.StatusBadRequest, err); !success { return } - if success := successOrAbort(ctx, http.StatusInternalServerError, h.Dispatcher.DeregisterApplication(application.MatrixID)); !success { + log.Printf("Deleting application %s.\n", application.Name) + + if success := successOrAbort(ctx, http.StatusInternalServerError, h.Dispatcher.DeregisterApplication(application)); !success { return } diff --git a/api/user.go b/api/user.go index 4793eb5..b896801 100644 --- a/api/user.go +++ b/api/user.go @@ -2,6 +2,7 @@ package api import ( "errors" + "log" "net/http" "github.com/eikendev/pushbits/model" @@ -12,12 +13,21 @@ import ( // The UserDatabase interface for encapsulating database access. type UserDatabase interface { CreateUser(user *model.User) error + DeleteUser(user *model.User) error + GetUserByID(ID uint) (*model.User, error) GetUserByName(name string) (*model.User, error) + GetApplications(user *model.User) ([]model.Application, error) +} + +// The UserDispatcher interface for relaying notifications. +type UserDispatcher interface { + DeregisterApplication(a *model.Application) error } // UserHandler holds information for processing requests about users. type UserHandler struct { - DB UserDatabase + DB UserDatabase + Dispatcher ApplicationDispatcher } func (h *UserHandler) userExists(name string) bool { @@ -46,3 +56,36 @@ func (h *UserHandler) CreateUser(ctx *gin.Context) { ctx.JSON(http.StatusOK, user.IntoExternalUser()) } + +// DeleteUser deletes a user with a certain ID. +func (h *UserHandler) DeleteUser(ctx *gin.Context) { + var deleteUser model.DeleteUser + + if success := successOrAbort(ctx, http.StatusBadRequest, ctx.BindUri(&deleteUser)); !success { + return + } + + user, err := h.DB.GetUserByID(deleteUser.ID) + if success := successOrAbort(ctx, http.StatusBadRequest, err); !success { + return + } + + log.Printf("Deleting user %s.\n", user.Name) + + applications, err := h.DB.GetApplications(user) + if success := successOrAbort(ctx, http.StatusInternalServerError, err); !success { + return + } + + for _, app := range applications { + if success := successOrAbort(ctx, http.StatusInternalServerError, h.Dispatcher.DeregisterApplication(&app)); !success { + return + } + } + + if success := successOrAbort(ctx, http.StatusInternalServerError, h.DB.DeleteUser(user)); !success { + return + } + + ctx.JSON(http.StatusOK, gin.H{}) +} diff --git a/configuration/configuration.go b/configuration/configuration.go index 9274b8a..0644462 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -28,7 +28,7 @@ func configFiles() []string { // Get returns the configuration extracted from env variables or config file. func Get() *Configuration { - config := new(Configuration) + config := &Configuration{} err := configor.New(&configor.Config{ Environment: "production", diff --git a/database/application.go b/database/application.go index 66b4065..fa4c20d 100644 --- a/database/application.go +++ b/database/application.go @@ -18,31 +18,28 @@ func (d *Database) DeleteApplication(application *model.Application) error { return d.gormdb.Delete(application).Error } -// UpdateApplication updates an application. -func (d *Database) UpdateApplication(app *model.Application) error { - return d.gormdb.Save(app).Error -} - -// GetApplicationByID returns the application for the given ID or nil. +// GetApplicationByID returns the application with the given ID or nil. func (d *Database) GetApplicationByID(ID uint) (*model.Application, error) { - app := new(model.Application) + var app model.Application + err := d.gormdb.First(&app, ID).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, err } - return app, err + return &app, err } -// GetApplicationByToken returns the application for the given token or nil. +// GetApplicationByToken returns the application with the given token or nil. func (d *Database) GetApplicationByToken(token string) (*model.Application, error) { - app := new(model.Application) - err := d.gormdb.Where("token = ?", token).First(app).Error + var app model.Application + + err := d.gormdb.Where("token = ?", token).First(&app).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, err } - return app, err + return &app, err } diff --git a/database/database.go b/database/database.go index c4a5b1d..c00aba2 100644 --- a/database/database.go +++ b/database/database.go @@ -78,8 +78,9 @@ func (d *Database) Close() { // Populate fills the database with initial information like the admin user. func (d *Database) Populate(name, password, matrixID string) error { - user := new(model.User) - query := d.gormdb.Where("name = ?", name).First(user) + var user model.User + + query := d.gormdb.Where("name = ?", name).First(&user) if errors.Is(query.Error, gorm.ErrRecordNotFound) { user := model.NewUser(name, password, true, matrixID) diff --git a/database/user.go b/database/user.go index 99fa356..ad552ef 100644 --- a/database/user.go +++ b/database/user.go @@ -13,14 +13,46 @@ func (d *Database) CreateUser(user *model.User) error { return d.gormdb.Create(user).Error } -// GetUserByName returns the user by the given name or nil. -func (d *Database) GetUserByName(name string) (*model.User, error) { - user := new(model.User) - err := d.gormdb.Where("name = ?", name).First(user).Error +// DeleteUser deletes a user. +func (d *Database) DeleteUser(user *model.User) error { + if err := d.gormdb.Where("user_id = ?", user.ID).Delete(model.Application{}).Error; err != nil { + return err + } + + return d.gormdb.Delete(user).Error +} + +// GetUserByID returns the user with the given ID or nil. +func (d *Database) GetUserByID(ID uint) (*model.User, error) { + var user model.User + + err := d.gormdb.First(&user, ID).Error if errors.Is(err, gorm.ErrRecordNotFound) { return nil, err } - return user, err + return &user, err +} + +// GetUserByName returns the user with the given name or nil. +func (d *Database) GetUserByName(name string) (*model.User, error) { + var user model.User + + err := d.gormdb.Where("name = ?", name).First(&user).Error + + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + return &user, err +} + +// GetApplications returns the applications associated with a given user. +func (d *Database) GetApplications(user *model.User) ([]model.Application, error) { + var applications []model.Application + + err := d.gormdb.Model(user).Association("Applications").Find(&applications) + + return applications, err } diff --git a/dispatcher/application.go b/dispatcher/application.go index f8c1c14..2da27d0 100644 --- a/dispatcher/application.go +++ b/dispatcher/application.go @@ -3,6 +3,8 @@ package dispatcher import ( "log" + "github.com/eikendev/pushbits/model" + "github.com/matrix-org/gomatrix" ) @@ -29,10 +31,10 @@ func (d *Dispatcher) RegisterApplication(name, user string) (string, error) { } // DeregisterApplication deletes a channel for an application. -func (d *Dispatcher) DeregisterApplication(matrixID string) error { - log.Printf("Deregistering application with ID %s.\n", matrixID) +func (d *Dispatcher) DeregisterApplication(a *model.Application) error { + log.Printf("Deregistering application with ID %s.\n", a.MatrixID) - _, err := d.client.LeaveRoom(matrixID) + _, err := d.client.LeaveRoom(a.MatrixID) if err != nil { log.Fatal(err) diff --git a/dispatcher/dispatcher.go b/dispatcher/dispatcher.go index 9dc6a9e..fb126f8 100644 --- a/dispatcher/dispatcher.go +++ b/dispatcher/dispatcher.go @@ -3,8 +3,6 @@ package dispatcher import ( "log" - "github.com/eikendev/pushbits/model" - "github.com/matrix-org/gomatrix" ) @@ -14,7 +12,6 @@ var ( // The Database interface for encapsulating database access. type Database interface { - UpdateApplication(application *model.Application) error } // Dispatcher holds information for sending notifications to clients. diff --git a/model/user.go b/model/user.go index 6d81e35..86d8057 100644 --- a/model/user.go +++ b/model/user.go @@ -68,3 +68,8 @@ func (u *User) IntoExternalUser() *ExternalUser { MatrixID: u.MatrixID, } } + +// DeleteUser is used to process queries for deleting users. +type DeleteUser struct { + ID uint `uri:"id"` +} diff --git a/router/router.go b/router/router.go index 2977ead..ca8c866 100644 --- a/router/router.go +++ b/router/router.go @@ -20,7 +20,7 @@ func Create(db *database.Database, dp *dispatcher.Dispatcher) *gin.Engine { applicationHandler := api.ApplicationHandler{DB: db, Dispatcher: dp} notificationHandler := api.NotificationHandler{DB: db, Dispatcher: dp} - userHandler := api.UserHandler{DB: db} + userHandler := api.UserHandler{DB: db, Dispatcher: dp} r := gin.Default() r.Use(location.Default()) @@ -38,7 +38,7 @@ func Create(db *database.Database, dp *dispatcher.Dispatcher) *gin.Engine { userGroup.Use(auth.RequireAdmin()) { userGroup.POST("", userHandler.CreateUser) - //userGroup.DELETE("/:id", userHandler.DeleteUser) + userGroup.DELETE("/:id", userHandler.DeleteUser) } return r