mirror of
https://github.com/fleetdm/fleet
synced 2026-05-20 15:38:39 +00:00
#21315 Ensures VPP uploads are behind premium license. Also moved the VPP service methods to the correct file
315 lines
9 KiB
Go
315 lines
9 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"mime/multipart"
|
|
"net/http"
|
|
|
|
"github.com/docker/go-units"
|
|
"github.com/fleetdm/fleet/v4/server/fleet"
|
|
)
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Get App Store apps
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
type getAppStoreAppsRequest struct {
|
|
TeamID uint `query:"team_id"`
|
|
}
|
|
|
|
type getAppStoreAppsResponse struct {
|
|
AppStoreApps []*fleet.VPPApp `json:"app_store_apps"`
|
|
Err error `json:"error,omitempty"`
|
|
}
|
|
|
|
func (r getAppStoreAppsResponse) error() error { return r.Err }
|
|
|
|
func getAppStoreAppsEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
|
req := request.(*getAppStoreAppsRequest)
|
|
apps, err := svc.GetAppStoreApps(ctx, &req.TeamID)
|
|
if err != nil {
|
|
return &getAppStoreAppsResponse{Err: err}, nil
|
|
}
|
|
|
|
return &getAppStoreAppsResponse{AppStoreApps: apps}, nil
|
|
}
|
|
|
|
func (svc *Service) GetAppStoreApps(ctx context.Context, teamID *uint) ([]*fleet.VPPApp, error) {
|
|
// skipauth: No authorization check needed due to implementation returning
|
|
// only license error.
|
|
svc.authz.SkipAuthorization(ctx)
|
|
|
|
return nil, fleet.ErrMissingLicense
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
// Add App Store apps
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
type addAppStoreAppRequest struct {
|
|
TeamID *uint `json:"team_id"`
|
|
AppStoreID string `json:"app_store_id"`
|
|
Platform fleet.AppleDevicePlatform `json:"platform"`
|
|
SelfService bool `json:"self_service"`
|
|
}
|
|
|
|
type addAppStoreAppResponse struct {
|
|
Err error `json:"error,omitempty"`
|
|
}
|
|
|
|
func (r addAppStoreAppResponse) error() error { return r.Err }
|
|
|
|
func addAppStoreAppEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
|
req := request.(*addAppStoreAppRequest)
|
|
err := svc.AddAppStoreApp(ctx, req.TeamID, fleet.VPPAppTeam{VPPAppID: fleet.VPPAppID{AdamID: req.AppStoreID, Platform: req.Platform}, SelfService: req.SelfService})
|
|
if err != nil {
|
|
return &addAppStoreAppResponse{Err: err}, nil
|
|
}
|
|
|
|
return &addAppStoreAppResponse{}, nil
|
|
}
|
|
|
|
func (svc *Service) AddAppStoreApp(ctx context.Context, _ *uint, _ fleet.VPPAppTeam) error {
|
|
// skipauth: No authorization check needed due to implementation returning
|
|
// only license error.
|
|
svc.authz.SkipAuthorization(ctx)
|
|
|
|
return fleet.ErrMissingLicense
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// POST /api/_version_/vpp_tokens
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
type uploadVPPTokenRequest struct {
|
|
File *multipart.FileHeader
|
|
}
|
|
|
|
func (uploadVPPTokenRequest) DecodeRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
|
decoded := uploadVPPTokenRequest{}
|
|
|
|
err := r.ParseMultipartForm(512 * units.MiB)
|
|
if err != nil {
|
|
return nil, &fleet.BadRequestError{
|
|
Message: "failed to parse multipart form",
|
|
InternalErr: err,
|
|
}
|
|
}
|
|
|
|
if r.MultipartForm.File["token"] == nil || len(r.MultipartForm.File["token"]) == 0 {
|
|
return nil, &fleet.BadRequestError{
|
|
Message: "token multipart field is required",
|
|
InternalErr: err,
|
|
}
|
|
}
|
|
|
|
decoded.File = r.MultipartForm.File["token"][0]
|
|
|
|
return &decoded, nil
|
|
}
|
|
|
|
type uploadVPPTokenResponse struct {
|
|
Err error `json:"error,omitempty"`
|
|
Token *fleet.VPPTokenDB `json:"token,omitempty"`
|
|
}
|
|
|
|
func (r uploadVPPTokenResponse) Status() int { return http.StatusAccepted }
|
|
|
|
func (r uploadVPPTokenResponse) error() error {
|
|
return r.Err
|
|
}
|
|
|
|
func uploadVPPTokenEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
|
req := request.(*uploadVPPTokenRequest)
|
|
file, err := req.File.Open()
|
|
if err != nil {
|
|
return uploadVPPTokenResponse{Err: err}, nil
|
|
}
|
|
defer file.Close()
|
|
|
|
tok, err := svc.UploadVPPToken(ctx, file)
|
|
if err != nil {
|
|
return uploadVPPTokenResponse{Err: err}, nil
|
|
}
|
|
|
|
return uploadVPPTokenResponse{Token: tok}, nil
|
|
}
|
|
|
|
func (svc *Service) UploadVPPToken(ctx context.Context, file io.ReadSeeker) (*fleet.VPPTokenDB, error) {
|
|
// skipauth: No authorization check needed due to implementation returning
|
|
// only license error.
|
|
svc.authz.SkipAuthorization(ctx)
|
|
|
|
return nil, fleet.ErrMissingLicense
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
// PATCH /api/_version_/fleet/vpp_tokens/%d/renew //
|
|
////////////////////////////////////////////////////
|
|
|
|
type patchVPPTokenRenewRequest struct {
|
|
ID uint `url:"id"`
|
|
File *multipart.FileHeader
|
|
}
|
|
|
|
func (patchVPPTokenRenewRequest) DecodeRequest(ctx context.Context, r *http.Request) (interface{}, error) {
|
|
decoded := patchVPPTokenRenewRequest{}
|
|
|
|
err := r.ParseMultipartForm(512 * units.MiB)
|
|
if err != nil {
|
|
return nil, &fleet.BadRequestError{
|
|
Message: "failed to parse multipart form",
|
|
InternalErr: err,
|
|
}
|
|
}
|
|
|
|
if r.MultipartForm.File["token"] == nil || len(r.MultipartForm.File["token"]) == 0 {
|
|
return nil, &fleet.BadRequestError{
|
|
Message: "token multipart field is required",
|
|
InternalErr: err,
|
|
}
|
|
}
|
|
|
|
decoded.File = r.MultipartForm.File["token"][0]
|
|
|
|
return &decoded, nil
|
|
}
|
|
|
|
type patchVPPTokenRenewResponse struct {
|
|
Err error `json:"error,omitempty"`
|
|
Token *fleet.VPPTokenDB `json:"token,omitempty"`
|
|
}
|
|
|
|
func (r patchVPPTokenRenewResponse) Status() int { return http.StatusAccepted }
|
|
|
|
func (r patchVPPTokenRenewResponse) error() error {
|
|
return r.Err
|
|
}
|
|
|
|
func patchVPPTokenRenewEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) {
|
|
req := request.(*patchVPPTokenRenewRequest)
|
|
file, err := req.File.Open()
|
|
if err != nil {
|
|
return patchVPPTokenRenewResponse{Err: err}, nil
|
|
}
|
|
defer file.Close()
|
|
|
|
tok, err := svc.UpdateVPPToken(ctx, req.ID, file)
|
|
if err != nil {
|
|
return patchVPPTokenRenewResponse{Err: err}, nil
|
|
}
|
|
|
|
return patchVPPTokenRenewResponse{Token: tok}, nil
|
|
}
|
|
|
|
func (svc *Service) UpdateVPPToken(ctx context.Context, tokenID uint, token io.ReadSeeker) (*fleet.VPPTokenDB, error) {
|
|
// skipauth: No authorization check needed due to implementation returning
|
|
// only license error.
|
|
svc.authz.SkipAuthorization(ctx)
|
|
|
|
return nil, fleet.ErrMissingLicense
|
|
}
|
|
|
|
////////////////////////////////////////////////////
|
|
// PATCH /api/_version_/fleet/vpp_tokens/%d/teams //
|
|
////////////////////////////////////////////////////
|
|
|
|
type patchVPPTokensTeamsRequest struct {
|
|
ID uint `url:"id"`
|
|
TeamIDs []uint `json:"teams"`
|
|
}
|
|
|
|
type patchVPPTokensTeamsResponse struct {
|
|
Token *fleet.VPPTokenDB `json:"token,omitempty"`
|
|
Err error `json:"error,omitempty"`
|
|
}
|
|
|
|
func (r patchVPPTokensTeamsResponse) error() error { return r.Err }
|
|
|
|
func patchVPPTokensTeams(ctx context.Context, request any, svc fleet.Service) (errorer, error) {
|
|
req := request.(*patchVPPTokensTeamsRequest)
|
|
|
|
tok, err := svc.UpdateVPPTokenTeams(ctx, req.ID, req.TeamIDs)
|
|
if err != nil {
|
|
return patchVPPTokensTeamsResponse{Err: err}, nil
|
|
}
|
|
return patchVPPTokensTeamsResponse{Token: tok}, nil
|
|
}
|
|
|
|
func (svc *Service) UpdateVPPTokenTeams(ctx context.Context, tokenID uint, teamIDs []uint) (*fleet.VPPTokenDB, error) {
|
|
// skipauth: No authorization check needed due to implementation returning
|
|
// only license error.
|
|
svc.authz.SkipAuthorization(ctx)
|
|
|
|
return nil, fleet.ErrMissingLicense
|
|
}
|
|
|
|
/////////////////////////////////////////
|
|
// GET /api/_version_/fleet/vpp_tokens //
|
|
/////////////////////////////////////////
|
|
|
|
type getVPPTokensRequest struct{}
|
|
|
|
type getVPPTokensResponse struct {
|
|
Tokens []*fleet.VPPTokenDB `json:"vpp_tokens"`
|
|
Err error `json:"error,omitempty"`
|
|
}
|
|
|
|
func (r getVPPTokensResponse) error() error { return r.Err }
|
|
|
|
func getVPPTokens(ctx context.Context, request any, svc fleet.Service) (errorer, error) {
|
|
tokens, err := svc.GetVPPTokens(ctx)
|
|
if err != nil {
|
|
return getVPPTokensResponse{Err: err}, nil
|
|
}
|
|
|
|
if tokens == nil {
|
|
tokens = []*fleet.VPPTokenDB{}
|
|
}
|
|
|
|
return getVPPTokensResponse{Tokens: tokens}, nil
|
|
}
|
|
|
|
func (svc *Service) GetVPPTokens(ctx context.Context) ([]*fleet.VPPTokenDB, error) {
|
|
// skipauth: No authorization check needed due to implementation returning
|
|
// only license error.
|
|
svc.authz.SkipAuthorization(ctx)
|
|
|
|
return nil, fleet.ErrMissingLicense
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// DELETE /api/_version_/fleet/vpp_tokens/%d //
|
|
///////////////////////////////////////////////
|
|
|
|
type deleteVPPTokenRequest struct {
|
|
ID uint `url:"id"`
|
|
}
|
|
|
|
type deleteVPPTokenResponse struct {
|
|
Err error `json:"error,omitempty"`
|
|
}
|
|
|
|
func (r deleteVPPTokenResponse) error() error { return r.Err }
|
|
|
|
func (r deleteVPPTokenResponse) Status() int { return http.StatusNoContent }
|
|
|
|
func deleteVPPToken(ctx context.Context, request any, svc fleet.Service) (errorer, error) {
|
|
req := request.(*deleteVPPTokenRequest)
|
|
|
|
err := svc.DeleteVPPToken(ctx, req.ID)
|
|
if err != nil {
|
|
return deleteVPPTokenResponse{Err: err}, nil
|
|
}
|
|
|
|
return deleteVPPTokenResponse{}, nil
|
|
}
|
|
|
|
func (svc *Service) DeleteVPPToken(ctx context.Context, tokenID uint) error {
|
|
// skipauth: No authorization check needed due to implementation returning
|
|
// only license error.
|
|
svc.authz.SkipAuthorization(ctx)
|
|
|
|
return fleet.ErrMissingLicense
|
|
}
|