The resetPassword struct lacked a json tag, causing Go to serialize the field as "Password" (capital P) while the frontend read "password" (lowercase), always getting undefined. Also display the generated password in a copyable input field as a fallback when clipboard access fails.
331 lines
11 KiB
Go
331 lines
11 KiB
Go
// This file is part of the happyDomain (R) project.
|
|
// Copyright (c) 2020-2024 happyDomain
|
|
// Authors: Pierre-Olivier Mercier, et al.
|
|
//
|
|
// This program is offered under a commercial and under the AGPL license.
|
|
// For commercial licensing, contact us at <contact@happydomain.org>.
|
|
//
|
|
// For AGPL licensing:
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
package controller
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"git.happydns.org/happyDomain/internal/api/middleware"
|
|
"git.happydns.org/happyDomain/internal/helpers"
|
|
happydns "git.happydns.org/happyDomain/model"
|
|
)
|
|
|
|
type AuthUserController struct {
|
|
auService happydns.AuthUserUsecase
|
|
adminService happydns.AdminAuthUserUsecase
|
|
}
|
|
|
|
func NewAuthUserController(auService happydns.AuthUserUsecase, adminService happydns.AdminAuthUserUsecase) *AuthUserController {
|
|
return &AuthUserController{
|
|
auService: auService,
|
|
adminService: adminService,
|
|
}
|
|
}
|
|
|
|
func (ac *AuthUserController) AuthUserHandler(c *gin.Context) {
|
|
user, err := middleware.AuthUserHandlerBase(ac.auService, c)
|
|
if err != nil {
|
|
user, err = ac.auService.GetAuthUserByEmail(c.Param("uid"))
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusNotFound, happydns.ErrorResponse{Message: "User not found"})
|
|
return
|
|
}
|
|
}
|
|
|
|
c.Set("authuser", user)
|
|
|
|
c.Next()
|
|
}
|
|
|
|
// GetAuthUsers retrieves a list of all registered users.
|
|
//
|
|
// @Summary List all users
|
|
// @Schemes
|
|
// @Description Retrieve a list of all registered users in the system.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {array} happydns.UserAuth
|
|
// @Failure 500 {object} happydns.ErrorResponse
|
|
// @Router /auth [get]
|
|
func (ac *AuthUserController) GetAuthUsers(c *gin.Context) {
|
|
users, err := ac.adminService.ListAllAuthUsers()
|
|
happydns.ApiResponse(c, users, err)
|
|
}
|
|
|
|
// NewAuthUser creates a new user account.
|
|
//
|
|
// @Summary Create new user
|
|
// @Schemes
|
|
// @Description Create a new user account in the system.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param body body happydns.UserAuth true "User data"
|
|
// @Success 200 {object} happydns.UserAuth
|
|
// @Failure 400 {object} happydns.ErrorResponse "Invalid input"
|
|
// @Failure 500 {object} happydns.ErrorResponse
|
|
// @Router /auth [post]
|
|
func (ac *AuthUserController) NewAuthUser(c *gin.Context) {
|
|
uu := &happydns.UserAuth{}
|
|
err := c.ShouldBindJSON(&uu)
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, happydns.ErrorResponse{Message: fmt.Sprintf("Something is wrong in received data: %s", err.Error())})
|
|
return
|
|
}
|
|
uu.Id = []byte{}
|
|
|
|
happydns.ApiResponse(c, uu, ac.adminService.AdminCreateAuthUser(uu))
|
|
}
|
|
|
|
// DeleteAuthUsers deletes all user accounts.
|
|
//
|
|
// @Summary Delete all users
|
|
// @Schemes
|
|
// @Description Delete all user accounts from the system.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Success 200 {boolean} true
|
|
// @Failure 500 {object} happydns.ErrorResponse
|
|
// @Router /auth [delete]
|
|
func (ac *AuthUserController) DeleteAuthUsers(c *gin.Context) {
|
|
happydns.ApiResponse(c, true, ac.adminService.ClearAuthUsers())
|
|
}
|
|
|
|
// GetAuthUser retrieves a specific user by identifier.
|
|
//
|
|
// @Summary Get user details
|
|
// @Schemes
|
|
// @Description Retrieve details for a specific user account.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param uid path string true "User identifier (email or ID)"
|
|
// @Success 200 {object} happydns.UserAuth
|
|
// @Failure 404 {object} happydns.ErrorResponse "User not found"
|
|
// @Router /auth/{uid} [get]
|
|
func (ac *AuthUserController) GetAuthUser(c *gin.Context) {
|
|
user := c.MustGet("authuser").(*happydns.UserAuth)
|
|
|
|
c.JSON(http.StatusOK, user)
|
|
}
|
|
|
|
// UpdateAuthUser updates an existing user account.
|
|
//
|
|
// @Summary Update user
|
|
// @Schemes
|
|
// @Description Update an existing user account's information.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param uid path string true "User identifier (email or ID)"
|
|
// @Param body body happydns.UserAuth true "Updated user data"
|
|
// @Success 200 {object} happydns.UserAuth
|
|
// @Failure 400 {object} happydns.ErrorResponse "Invalid input"
|
|
// @Failure 404 {object} happydns.ErrorResponse "User not found"
|
|
// @Failure 500 {object} happydns.ErrorResponse
|
|
// @Router /auth/{uid} [put]
|
|
func (ac *AuthUserController) UpdateAuthUser(c *gin.Context) {
|
|
user := c.MustGet("authuser").(*happydns.UserAuth)
|
|
|
|
uu := &happydns.UserAuth{}
|
|
err := c.ShouldBindJSON(&uu)
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, happydns.ErrorResponse{Message: fmt.Sprintf("Something is wrong in received data: %s", err.Error())})
|
|
return
|
|
}
|
|
uu.Id = user.Id
|
|
|
|
happydns.ApiResponse(c, uu, ac.adminService.AdminUpdateAuthUser(uu))
|
|
}
|
|
|
|
// DeleteAuthUser deletes a specific user account.
|
|
//
|
|
// @Summary Delete user
|
|
// @Schemes
|
|
// @Description Delete a specific user account from the system.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param uid path string true "User identifier (email or ID)"
|
|
// @Success 200 {boolean} true
|
|
// @Failure 404 {object} happydns.ErrorResponse "User not found"
|
|
// @Failure 500 {object} happydns.ErrorResponse
|
|
// @Router /auth/{uid} [delete]
|
|
func (ac *AuthUserController) DeleteAuthUser(c *gin.Context) {
|
|
user := c.MustGet("authuser").(*happydns.UserAuth)
|
|
|
|
happydns.ApiResponse(c, true, ac.adminService.AdminDeleteAuthUser(user))
|
|
}
|
|
|
|
// EmailValidationLink generates an email validation link for a user.
|
|
//
|
|
// @Summary Generate email validation link
|
|
// @Schemes
|
|
// @Description Generate a validation link for verifying user's email address.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param uid path string true "User identifier (email or ID)"
|
|
// @Success 200 {string} string "Validation link"
|
|
// @Failure 404 {object} happydns.ErrorResponse "User not found"
|
|
// @Failure 500 {object} happydns.ErrorResponse
|
|
// @Router /auth/{uid}/validation_link [post]
|
|
func (ac *AuthUserController) EmailValidationLink(c *gin.Context) {
|
|
user := c.MustGet("authuser").(*happydns.UserAuth)
|
|
|
|
link, err := ac.auService.GenerateValidationLink(user)
|
|
happydns.ApiResponse(c, link, err)
|
|
}
|
|
|
|
// RecoverUserAcct generates an account recovery link for a user.
|
|
//
|
|
// @Summary Generate account recovery link
|
|
// @Schemes
|
|
// @Description Generate a recovery link for user account access.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param uid path string true "User identifier (email or ID)"
|
|
// @Success 200 {string} string "Recovery link"
|
|
// @Failure 404 {object} happydns.ErrorResponse "User not found"
|
|
// @Failure 500 {object} happydns.ErrorResponse
|
|
// @Router /auth/{uid}/recover_link [post]
|
|
func (ac *AuthUserController) RecoverUserAcct(c *gin.Context) {
|
|
user := c.MustGet("authuser").(*happydns.UserAuth)
|
|
|
|
link, err := ac.auService.GenerateRecoveryLink(user)
|
|
happydns.ApiResponse(c, link, err)
|
|
}
|
|
|
|
type resetPassword struct {
|
|
Password string `json:"password"`
|
|
}
|
|
|
|
// ResetUserPasswd resets a user's password.
|
|
//
|
|
// @Summary Reset user password
|
|
// @Schemes
|
|
// @Description Reset a user's password. If no password is provided, a random one will be generated.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param uid path string true "User identifier (email or ID)"
|
|
// @Param body body resetPassword true "New password (optional)"
|
|
// @Success 200 {object} resetPassword "New password"
|
|
// @Failure 400 {object} happydns.ErrorResponse "Invalid input"
|
|
// @Failure 404 {object} happydns.ErrorResponse "User not found"
|
|
// @Failure 406 {object} happydns.ErrorResponse "Password identical to current"
|
|
// @Failure 500 {object} happydns.ErrorResponse
|
|
// @Router /auth/{uid}/reset_password [post]
|
|
func (ac *AuthUserController) ResetUserPasswd(c *gin.Context) {
|
|
user := c.MustGet("authuser").(*happydns.UserAuth)
|
|
|
|
urp := &resetPassword{}
|
|
err := c.ShouldBindJSON(urp)
|
|
if err != nil && !errors.Is(err, io.EOF) {
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, happydns.ErrorResponse{Message: fmt.Sprintf("Something is wrong in received data: %s", err.Error())})
|
|
return
|
|
}
|
|
|
|
if urp.Password == "" {
|
|
urp.Password, err = helpers.GeneratePassword()
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, happydns.ErrorResponse{Message: err.Error()})
|
|
return
|
|
}
|
|
} else if user.CheckPassword(urp.Password) {
|
|
c.AbortWithStatusJSON(http.StatusNotAcceptable, happydns.ErrorResponse{Message: "The reset password is identical to the current password"})
|
|
return
|
|
}
|
|
|
|
err = user.DefinePassword(urp.Password)
|
|
if err != nil {
|
|
c.AbortWithStatusJSON(http.StatusInternalServerError, happydns.ErrorResponse{Message: err.Error()})
|
|
return
|
|
}
|
|
|
|
happydns.ApiResponse(c, urp, ac.adminService.AdminUpdateAuthUser(user))
|
|
}
|
|
|
|
// SendRecoverUserAcct sends an account recovery email to the user.
|
|
//
|
|
// @Summary Send account recovery email
|
|
// @Schemes
|
|
// @Description Send an account recovery link to the user's email address.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param uid path string true "User identifier (email or ID)"
|
|
// @Success 200 {boolean} true
|
|
// @Failure 404 {object} happydns.ErrorResponse "User not found"
|
|
// @Failure 500 {object} happydns.ErrorResponse
|
|
// @Router /auth/{uid}/send_recover_email [post]
|
|
func (ac *AuthUserController) SendRecoverUserAcct(c *gin.Context) {
|
|
user := c.MustGet("authuser").(*happydns.UserAuth)
|
|
|
|
happydns.ApiResponse(c, true, ac.auService.SendRecoveryLink(user))
|
|
}
|
|
|
|
// SendValidateUserEmail sends an email validation link to the user.
|
|
//
|
|
// @Summary Send email validation link
|
|
// @Schemes
|
|
// @Description Send an email validation link to the user's email address.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param uid path string true "User identifier (email or ID)"
|
|
// @Success 200 {boolean} true
|
|
// @Failure 404 {object} happydns.ErrorResponse "User not found"
|
|
// @Failure 500 {object} happydns.ErrorResponse
|
|
// @Router /auth/{uid}/send_validation_email [post]
|
|
func (ac *AuthUserController) SendValidateUserEmail(c *gin.Context) {
|
|
user := c.MustGet("authuser").(*happydns.UserAuth)
|
|
|
|
happydns.ApiResponse(c, true, ac.auService.SendValidationLink(user))
|
|
}
|
|
|
|
// ValidateEmail marks a user's email as verified.
|
|
//
|
|
// @Summary Validate user email
|
|
// @Schemes
|
|
// @Description Mark a user's email address as verified.
|
|
// @Tags admin-users
|
|
// @Accept json
|
|
// @Produce json
|
|
// @Param uid path string true "User identifier (email or ID)"
|
|
// @Success 200 {object} happydns.UserAuth
|
|
// @Failure 404 {object} happydns.ErrorResponse "User not found"
|
|
// @Failure 500 {object} happydns.ErrorResponse
|
|
// @Router /auth/{uid}/validate_email [post]
|
|
func (ac *AuthUserController) ValidateEmail(c *gin.Context) {
|
|
user := c.MustGet("authuser").(*happydns.UserAuth)
|
|
|
|
happydns.ApiResponse(c, user, ac.adminService.MarkEmailValidated(user))
|
|
}
|