mirror of
https://github.com/h44z/wg-portal
synced 2025-06-26 18:16:21 +00:00
wip: tmp commit
This commit is contained in:
parent
612ae742b2
commit
04f87bf37a
@ -19,7 +19,7 @@
|
|||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"AdminBasicAuth": []
|
"BasicAuth": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"produces": [
|
"produces": [
|
||||||
@ -53,7 +53,7 @@
|
|||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"UserBasicAuth": []
|
"BasicAuth": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": "Normal users can only access their own record. Admins can access all records.",
|
"description": "Normal users can only access their own record. Admins can access all records.",
|
||||||
@ -183,10 +183,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"securityDefinitions": {
|
"securityDefinitions": {
|
||||||
"AdminBasicAuth": {
|
"BasicAuth": {
|
||||||
"type": "basic"
|
|
||||||
},
|
|
||||||
"UserBasicAuth": {
|
|
||||||
"type": "basic"
|
"type": "basic"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.Error'
|
$ref: '#/definitions/models.Error'
|
||||||
security:
|
security:
|
||||||
- AdminBasicAuth: []
|
- BasicAuth: []
|
||||||
summary: Get all user records.
|
summary: Get all user records.
|
||||||
tags:
|
tags:
|
||||||
- Users
|
- Users
|
||||||
@ -122,13 +122,11 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/models.Error'
|
$ref: '#/definitions/models.Error'
|
||||||
security:
|
security:
|
||||||
- UserBasicAuth: []
|
- BasicAuth: []
|
||||||
summary: Get a specific user record by its internal identifier.
|
summary: Get a specific user record by its internal identifier.
|
||||||
tags:
|
tags:
|
||||||
- Users
|
- Users
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
AdminBasicAuth:
|
BasicAuth:
|
||||||
type: basic
|
|
||||||
UserBasicAuth:
|
|
||||||
type: basic
|
type: basic
|
||||||
swagger: "2.0"
|
swagger: "2.0"
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
spec-url="{{ $.ApiSpecUrl }}"
|
spec-url="{{ $.ApiSpecUrl }}"
|
||||||
theme="dark"
|
theme="dark"
|
||||||
allow-server-selection="false"
|
allow-server-selection="false"
|
||||||
allow-authentication="false"
|
allow-authentication="true"
|
||||||
load-fonts="false"
|
load-fonts="false"
|
||||||
schema-expand-level="1"
|
schema-expand-level="1"
|
||||||
allow-spec-url-load="false"
|
allow-spec-url-load="false"
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/h44z/wg-portal/internal/app/users"
|
||||||
"github.com/h44z/wg-portal/internal/config"
|
"github.com/h44z/wg-portal/internal/config"
|
||||||
"github.com/h44z/wg-portal/internal/domain"
|
"github.com/h44z/wg-portal/internal/domain"
|
||||||
)
|
)
|
||||||
@ -71,17 +72,50 @@ func (s UserService) GetUsers(ctx context.Context) ([]domain.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s UserService) GetUserById(ctx context.Context, id domain.UserIdentifier) (*domain.User, error) {
|
func (s UserService) GetUserById(ctx context.Context, id domain.UserIdentifier) (*domain.User, error) {
|
||||||
|
if err := domain.ValidateUserAccessRights(ctx, id); err != nil {
|
||||||
|
return nil, errors.Join(err, domain.ErrNoPermission)
|
||||||
|
}
|
||||||
|
|
||||||
user, err := s.users.GetUser(ctx, id)
|
user, err := s.users.GetUser(ctx, id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to load user %s: %w", id, err)
|
return nil, fmt.Errorf("unable to load user %s: %w", id, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := domain.ValidateUserAccessRights(ctx, user.Identifier); err != nil {
|
|
||||||
return nil, errors.Join(err, domain.ErrNoPermission)
|
|
||||||
}
|
|
||||||
|
|
||||||
peers, _ := s.peers.GetUserPeers(ctx, user.Identifier) // ignore error, list will be empty in error case
|
peers, _ := s.peers.GetUserPeers(ctx, user.Identifier) // ignore error, list will be empty in error case
|
||||||
user.LinkedPeerCount = len(peers)
|
user.LinkedPeerCount = len(peers)
|
||||||
|
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s UserService) CreateUser(ctx context.Context, user *domain.User) (*domain.User, error) {
|
||||||
|
if err := domain.ValidateAdminAccessRights(ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
existingUser, err := s.users.GetUser(ctx, user.Identifier)
|
||||||
|
if err != nil && !errors.Is(err, domain.ErrNotFound) {
|
||||||
|
return nil, fmt.Errorf("unable to load existing user %s: %w", user.Identifier, err)
|
||||||
|
}
|
||||||
|
if existingUser != nil {
|
||||||
|
return nil, errors.Join(fmt.Errorf("user %s already exists", user.Identifier), domain.ErrDuplicateEntry)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := users.ValidateCreation(ctx, user); err != nil {
|
||||||
|
return nil, errors.Join(fmt.Errorf("creation not allowed: %w", err), domain.ErrInvalidData)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = user.HashPassword()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.users.SaveUser(ctx, user.Identifier, func(u *domain.User) (*domain.User, error) {
|
||||||
|
user.CopyCalculatedAttributes(u)
|
||||||
|
return user, nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("creation failure: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
@ -30,15 +30,7 @@ type Handler interface {
|
|||||||
// @contact.name WireGuard Portal Project
|
// @contact.name WireGuard Portal Project
|
||||||
// @contact.url https://github.com/h44z/wg-portal
|
// @contact.url https://github.com/h44z/wg-portal
|
||||||
|
|
||||||
// @securityDefinitions.basic AdminBasicAuth
|
// @securityDefinitions.basic BasicAuth
|
||||||
// @in header
|
|
||||||
// @name Authorization
|
|
||||||
// @scope.admin Admin access required
|
|
||||||
|
|
||||||
// @securityDefinitions.basic UserBasicAuth
|
|
||||||
// @in header
|
|
||||||
// @name Authorization
|
|
||||||
// @scope.user User access required
|
|
||||||
|
|
||||||
// @BasePath /api/v1
|
// @BasePath /api/v1
|
||||||
// @query.collection.format multi
|
// @query.collection.format multi
|
||||||
@ -74,6 +66,10 @@ func ParseServiceError(err error) (int, models.Error) {
|
|||||||
code = http.StatusNotFound
|
code = http.StatusNotFound
|
||||||
case errors.Is(err, domain.ErrNoPermission):
|
case errors.Is(err, domain.ErrNoPermission):
|
||||||
code = http.StatusForbidden
|
code = http.StatusForbidden
|
||||||
|
case errors.Is(err, domain.ErrDuplicateEntry):
|
||||||
|
code = http.StatusConflict
|
||||||
|
case errors.Is(err, domain.ErrInvalidData):
|
||||||
|
code = http.StatusBadRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
return code, models.Error{
|
return code, models.Error{
|
||||||
|
@ -5,6 +5,7 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/h44z/wg-portal/internal/app/api/v0/model"
|
||||||
"github.com/h44z/wg-portal/internal/app/api/v1/models"
|
"github.com/h44z/wg-portal/internal/app/api/v1/models"
|
||||||
"github.com/h44z/wg-portal/internal/domain"
|
"github.com/h44z/wg-portal/internal/domain"
|
||||||
)
|
)
|
||||||
@ -12,6 +13,7 @@ import (
|
|||||||
type UserService interface {
|
type UserService interface {
|
||||||
GetUsers(ctx context.Context) ([]domain.User, error)
|
GetUsers(ctx context.Context) ([]domain.User, error)
|
||||||
GetUserById(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
|
GetUserById(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
|
||||||
|
CreateUser(ctx context.Context, user *domain.User) (*domain.User, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserEndpoint struct {
|
type UserEndpoint struct {
|
||||||
@ -44,7 +46,7 @@ func (e UserEndpoint) RegisterRoutes(g *gin.RouterGroup, authenticator *authenti
|
|||||||
// @Success 200 {object} []models.User
|
// @Success 200 {object} []models.User
|
||||||
// @Failure 500 {object} models.Error
|
// @Failure 500 {object} models.Error
|
||||||
// @Router /user/all [get]
|
// @Router /user/all [get]
|
||||||
// @Security AdminBasicAuth
|
// @Security BasicAuth
|
||||||
func (e UserEndpoint) handleAllGet() gin.HandlerFunc {
|
func (e UserEndpoint) handleAllGet() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
ctx := domain.SetUserInfoFromGin(c)
|
ctx := domain.SetUserInfoFromGin(c)
|
||||||
@ -65,6 +67,7 @@ func (e UserEndpoint) handleAllGet() gin.HandlerFunc {
|
|||||||
// @Tags Users
|
// @Tags Users
|
||||||
// @Summary Get a specific user record by its internal identifier.
|
// @Summary Get a specific user record by its internal identifier.
|
||||||
// @Description Normal users can only access their own record. Admins can access all records.
|
// @Description Normal users can only access their own record. Admins can access all records.
|
||||||
|
// @Param id path string true "The user identifier."
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Success 200 {object} models.User
|
// @Success 200 {object} models.User
|
||||||
// @Failure 403 {object} models.Error
|
// @Failure 403 {object} models.Error
|
||||||
@ -72,7 +75,7 @@ func (e UserEndpoint) handleAllGet() gin.HandlerFunc {
|
|||||||
// @Failure 404 {object} models.Error
|
// @Failure 404 {object} models.Error
|
||||||
// @Failure 500 {object} models.Error
|
// @Failure 500 {object} models.Error
|
||||||
// @Router /user/id/{id} [get]
|
// @Router /user/id/{id} [get]
|
||||||
// @Security UserBasicAuth
|
// @Security BasicAuth
|
||||||
func (e UserEndpoint) handleByIdGet() gin.HandlerFunc {
|
func (e UserEndpoint) handleByIdGet() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
ctx := domain.SetUserInfoFromGin(c)
|
ctx := domain.SetUserInfoFromGin(c)
|
||||||
@ -89,6 +92,123 @@ func (e UserEndpoint) handleByIdGet() gin.HandlerFunc {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.JSON(http.StatusOK, models.NewUser(user))
|
c.JSON(http.StatusOK, models.NewUser(user, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleCreatePost returns a gorm handler function.
|
||||||
|
//
|
||||||
|
// @ID users_handleCreatePost
|
||||||
|
// @Tags Users
|
||||||
|
// @Summary Create a new user record.
|
||||||
|
// @Description Only admins can create new records.
|
||||||
|
// @Param request body models.User true "The user data."
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} models.User
|
||||||
|
// @Failure 400 {object} models.Error
|
||||||
|
// @Failure 403 {object} models.Error
|
||||||
|
// @Failure 409 {object} models.Error
|
||||||
|
// @Failure 500 {object} models.Error
|
||||||
|
// @Router /user/new [post]
|
||||||
|
func (e UserEndpoint) handleCreatePost() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
ctx := domain.SetUserInfoFromGin(c)
|
||||||
|
|
||||||
|
var user models.User
|
||||||
|
err := c.BindJSON(&user)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, models.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
newUser, err := e.users.CreateUser(ctx, models.NewDomainUser(&user))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(ParseServiceError(err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, models.NewUser(newUser, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleUpdatePut returns a gorm handler function.
|
||||||
|
//
|
||||||
|
// @ID users_handleUpdatePut
|
||||||
|
// @Tags Users
|
||||||
|
// @Summary Update a user record.
|
||||||
|
// @Description Only admins can update existing records.
|
||||||
|
// @Param id path string true "The user identifier"
|
||||||
|
// @Param request body models.User true "The user data"
|
||||||
|
// @Produce json
|
||||||
|
// @Success 200 {object} models.User
|
||||||
|
// @Failure 400 {object} models.Error
|
||||||
|
// @Failure 403 {object} models.Error
|
||||||
|
// @Failure 404 {object} models.Error
|
||||||
|
// @Failure 500 {object} models.Error
|
||||||
|
// @Router /user/{id} [put]
|
||||||
|
func (e UserEndpoint) handleUpdatePut() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// TODO: implement
|
||||||
|
ctx := domain.SetUserInfoFromGin(c)
|
||||||
|
|
||||||
|
id := Base64UrlDecode(c.Param("id"))
|
||||||
|
if id == "" {
|
||||||
|
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var user model.User
|
||||||
|
err := c.BindJSON(&user)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if id != user.Identifier {
|
||||||
|
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "user id mismatch"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
updateUser, err := e.app.UpdateUser(ctx, model.NewDomainUser(&user))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError,
|
||||||
|
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, model.NewUser(updateUser, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handleDelete returns a gorm handler function.
|
||||||
|
//
|
||||||
|
// @ID users_handleDelete
|
||||||
|
// @Tags Users
|
||||||
|
// @Summary Delete the user record.
|
||||||
|
// @Produce json
|
||||||
|
// @Param id path string true "The user identifier"
|
||||||
|
// @Success 204 "No content if deletion was successful"
|
||||||
|
// @Failure 400 {object} model.Error
|
||||||
|
// @Failure 500 {object} model.Error
|
||||||
|
// @Router /user/{id} [delete]
|
||||||
|
func (e UserEndpoint) handleDelete() gin.HandlerFunc {
|
||||||
|
return func(c *gin.Context) {
|
||||||
|
// TODO: implement
|
||||||
|
ctx := domain.SetUserInfoFromGin(c)
|
||||||
|
|
||||||
|
id := Base64UrlDecode(c.Param("id"))
|
||||||
|
if id == "" {
|
||||||
|
c.JSON(http.StatusBadRequest, model.Error{Code: http.StatusBadRequest, Message: "missing user id"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := e.app.DeleteUser(ctx, domain.UserIdentifier(id))
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError,
|
||||||
|
model.Error{Code: http.StatusInternalServerError, Message: err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Status(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,9 @@ func (h authenticationHandler) LoggedIn(scopes ...Scope) gin.HandlerFunc {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if user exists in DB
|
// check if user exists in DB
|
||||||
user, err := h.userSource.GetUser(c.Request.Context(), domain.UserIdentifier(username))
|
|
||||||
|
ctx := domain.SetUserInfo(c.Request.Context(), domain.SystemAdminContextUserInfo())
|
||||||
|
user, err := h.userSource.GetUser(ctx, domain.UserIdentifier(username))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// Abort the request with the appropriate error code
|
// Abort the request with the appropriate error code
|
||||||
c.Abort()
|
c.Abort()
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/h44z/wg-portal/internal/domain"
|
"github.com/h44z/wg-portal/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,18 +20,20 @@ type User struct {
|
|||||||
Department string `json:"Department"` // The department of the user. This field is optional.
|
Department string `json:"Department"` // The department of the user. This field is optional.
|
||||||
Notes string `json:"Notes"` // Additional notes about the user. This field is optional.
|
Notes string `json:"Notes"` // Additional notes about the user. This field is optional.
|
||||||
|
|
||||||
|
Password string `json:"Password,omitempty"` // The password of the user. This field is never populated on read operations.
|
||||||
Disabled bool `json:"Disabled"` // If this field is set, the user is disabled.
|
Disabled bool `json:"Disabled"` // If this field is set, the user is disabled.
|
||||||
DisabledReason string `json:"DisabledReason"` // The reason why the user has been disabled.
|
DisabledReason string `json:"DisabledReason"` // The reason why the user has been disabled.
|
||||||
Locked bool `json:"Locked"` // If this field is set, the user is locked and thus unable to log in to WireGuard Portal.
|
Locked bool `json:"Locked"` // If this field is set, the user is locked and thus unable to log in to WireGuard Portal.
|
||||||
LockedReason string `json:"LockedReason"` // The reason why the user has been locked.
|
LockedReason string `json:"LockedReason"` // The reason why the user has been locked.
|
||||||
|
|
||||||
ApiEnabled bool `json:"ApiEnabled"` // If this field is set, the user is allowed to use the RESTful API.
|
ApiToken string `json:"ApiToken"` // The API token of the user. This field is never populated on bulk read operations.
|
||||||
|
ApiEnabled bool `json:"ApiEnabled"` // If this field is set, the user is allowed to use the RESTful API. This field is read-only.
|
||||||
|
|
||||||
PeerCount int `json:"PeerCount"` // The number of peers linked to the user.
|
PeerCount int `json:"PeerCount"` // The number of peers linked to the user. This field is read-only.
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUser(src *domain.User) *User {
|
func NewUser(src *domain.User, exposeCredentials bool) *User {
|
||||||
return &User{
|
u := &User{
|
||||||
Identifier: string(src.Identifier),
|
Identifier: string(src.Identifier),
|
||||||
Email: src.Email,
|
Email: src.Email,
|
||||||
Source: string(src.Source),
|
Source: string(src.Source),
|
||||||
@ -40,21 +44,64 @@ func NewUser(src *domain.User) *User {
|
|||||||
Phone: src.Phone,
|
Phone: src.Phone,
|
||||||
Department: src.Department,
|
Department: src.Department,
|
||||||
Notes: src.Notes,
|
Notes: src.Notes,
|
||||||
|
Password: "", // never fill password
|
||||||
Disabled: src.IsDisabled(),
|
Disabled: src.IsDisabled(),
|
||||||
DisabledReason: src.DisabledReason,
|
DisabledReason: src.DisabledReason,
|
||||||
Locked: src.IsLocked(),
|
Locked: src.IsLocked(),
|
||||||
LockedReason: src.LockedReason,
|
LockedReason: src.LockedReason,
|
||||||
|
ApiToken: "", // by default, do not expose API token
|
||||||
ApiEnabled: src.IsApiEnabled(),
|
ApiEnabled: src.IsApiEnabled(),
|
||||||
|
|
||||||
PeerCount: src.LinkedPeerCount,
|
PeerCount: src.LinkedPeerCount,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if exposeCredentials {
|
||||||
|
u.ApiToken = src.ApiToken
|
||||||
|
}
|
||||||
|
|
||||||
|
return u
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUsers(src []domain.User) []User {
|
func NewUsers(src []domain.User) []User {
|
||||||
results := make([]User, len(src))
|
results := make([]User, len(src))
|
||||||
for i := range src {
|
for i := range src {
|
||||||
results[i] = *NewUser(&src[i])
|
results[i] = *NewUser(&src[i], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewDomainUser(src *User) *domain.User {
|
||||||
|
now := time.Now()
|
||||||
|
res := &domain.User{
|
||||||
|
Identifier: domain.UserIdentifier(src.Identifier),
|
||||||
|
Email: src.Email,
|
||||||
|
Source: domain.UserSource(src.Source),
|
||||||
|
ProviderName: src.ProviderName,
|
||||||
|
IsAdmin: src.IsAdmin,
|
||||||
|
Firstname: src.Firstname,
|
||||||
|
Lastname: src.Lastname,
|
||||||
|
Phone: src.Phone,
|
||||||
|
Department: src.Department,
|
||||||
|
Notes: src.Notes,
|
||||||
|
Password: domain.PrivateString(src.Password),
|
||||||
|
Disabled: nil, // set below
|
||||||
|
DisabledReason: src.DisabledReason,
|
||||||
|
Locked: nil, // set below
|
||||||
|
LockedReason: src.LockedReason,
|
||||||
|
}
|
||||||
|
|
||||||
|
if src.ApiToken != "" {
|
||||||
|
res.ApiToken = src.ApiToken
|
||||||
|
res.ApiTokenCreated = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
if src.Disabled {
|
||||||
|
res.Disabled = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
if src.Locked {
|
||||||
|
res.Locked = &now
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
@ -197,7 +197,7 @@ func (m Manager) CreateUser(ctx context.Context, user *domain.User) (*domain.Use
|
|||||||
return nil, fmt.Errorf("user %s already exists", user.Identifier)
|
return nil, fmt.Errorf("user %s already exists", user.Identifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := m.validateCreation(ctx, user); err != nil {
|
if err := ValidateCreation(ctx, user); err != nil {
|
||||||
return nil, fmt.Errorf("creation not allowed: %w", err)
|
return nil, fmt.Errorf("creation not allowed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -328,7 +328,7 @@ func (m Manager) validateModifications(ctx context.Context, old, new *domain.Use
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Manager) validateCreation(ctx context.Context, new *domain.User) error {
|
func ValidateCreation(ctx context.Context, new *domain.User) error {
|
||||||
currentUser := domain.GetUserInfo(ctx)
|
currentUser := domain.GetUserInfo(ctx)
|
||||||
|
|
||||||
if !currentUser.IsAdmin {
|
if !currentUser.IsAdmin {
|
||||||
@ -343,8 +343,12 @@ func (m Manager) validateCreation(ctx context.Context, new *domain.User) error {
|
|||||||
return fmt.Errorf("reserved user identifier")
|
return fmt.Errorf("reserved user identifier")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if new.Identifier == "new" { // the new user identifier collides with the rest api routes
|
||||||
|
return fmt.Errorf("reserved user identifier")
|
||||||
|
}
|
||||||
|
|
||||||
if new.Source != domain.UserSourceDatabase {
|
if new.Source != domain.UserSourceDatabase {
|
||||||
return fmt.Errorf("invalid user source: %s", new.Source)
|
return fmt.Errorf("invalid user source: %s, only %s is allowed", new.Source, domain.UserSourceDatabase)
|
||||||
}
|
}
|
||||||
|
|
||||||
if string(new.Password) == "" {
|
if string(new.Password) == "" {
|
||||||
|
@ -8,6 +8,8 @@ import (
|
|||||||
var ErrNotFound = errors.New("record not found")
|
var ErrNotFound = errors.New("record not found")
|
||||||
var ErrNotUnique = errors.New("record not unique")
|
var ErrNotUnique = errors.New("record not unique")
|
||||||
var ErrNoPermission = errors.New("no permission")
|
var ErrNoPermission = errors.New("no permission")
|
||||||
|
var ErrDuplicateEntry = errors.New("duplicate entry")
|
||||||
|
var ErrInvalidData = errors.New("invalid data")
|
||||||
|
|
||||||
// GetStackTrace returns a stack trace of the current goroutine. The stack trace has at most 1024 bytes.
|
// GetStackTrace returns a stack trace of the current goroutine. The stack trace has at most 1024 bytes.
|
||||||
func GetStackTrace() string {
|
func GetStackTrace() string {
|
||||||
|
Loading…
Reference in New Issue
Block a user