fleet/server/service/service_users.go
Victor Lyuboslavsky c88cc953fb
Refactor endpoint_utils for modularization (#36484)
Resolves #37192

Separating generic endpoint_utils middleware logic from domain-specific
business logic. New bounded contexts would share the generic logic and
implement their own domain-specific logic. The two approaches used in
this PR are:
- Use common `platform` types
- Use interfaces

In the next PR we will move `endpointer_utils`, `authzcheck` and
`ratelimit` into `platform` directory.

# Checklist for submitter

- [x] Added changes file

## Testing

- [x] Added/updated tests
- [x] QA'd all new/changed functionality manually



<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Restructured internal error handling and context management to support
bounded context architecture.
* Improved error context collection and telemetry observability through
a provider-based mechanism.
* Decoupled licensing and authentication concerns into interfaces for
better modularity.

* **Chores**
* Updated internal package dependencies to align with new architectural
boundaries.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-12-31 09:12:00 -06:00

82 lines
2.1 KiB
Go

package service
import (
"context"
"github.com/fleetdm/fleet/v4/server/authz"
"github.com/fleetdm/fleet/v4/server/contexts/ctxerr"
"github.com/fleetdm/fleet/v4/server/contexts/license"
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/ptr"
)
func (svc *Service) CreateInitialUser(ctx context.Context, p fleet.UserPayload) (*fleet.User, error) {
// skipauth: Only the initial user creation should be allowed to skip
// authorization (because there is not yet a user context to check against).
svc.authz.SkipAuthorization(ctx)
setupRequired, err := svc.SetupRequired(ctx)
if err != nil {
return nil, err
}
if !setupRequired {
return nil, ctxerr.New(ctx, "a user already exists")
}
// Initial user should be global admin with no explicit teams
p.GlobalRole = ptr.String(fleet.RoleAdmin)
p.Teams = nil
return svc.NewUser(ctx, p)
}
func (svc *Service) NewUser(ctx context.Context, p fleet.UserPayload) (*fleet.User, error) {
licChecker, _ := license.FromContext(ctx)
lic, _ := licChecker.(*fleet.LicenseInfo)
if lic == nil {
return nil, ctxerr.New(ctx, "license not found")
}
if err := fleet.ValidateUserRoles(true, p, *lic); err != nil {
return nil, ctxerr.Wrap(ctx, err, "validate role")
}
if !lic.IsPremium() {
p.MFAEnabled = ptr.Bool(false)
}
user, err := p.User(svc.config.Auth.SaltKeySize, svc.config.Auth.BcryptCost)
if err != nil {
return nil, err
}
user, err = svc.ds.NewUser(ctx, user)
if err != nil {
return nil, err
}
adminUser := authz.UserFromContext(ctx)
if adminUser == nil {
// In case of invites the user created herself.
adminUser = user
}
if err := svc.NewActivity(
ctx,
adminUser,
fleet.ActivityTypeCreatedUser{
UserID: user.ID,
UserName: user.Name,
UserEmail: user.Email,
},
); err != nil {
return nil, err
}
if err := fleet.LogRoleChangeActivities(ctx, svc, adminUser, nil, nil, user); err != nil {
return nil, err
}
return user, nil
}
func (svc *Service) UserUnauthorized(ctx context.Context, id uint) (*fleet.User, error) {
// Explicitly no authorization check. Should only be used by middleware.
return svc.ds.UserByID(ctx, id)
}