Validate Premium license when uploading VPP tokens (#21720)

#21315

Ensures VPP uploads are behind premium license.

Also moved the VPP service methods to the correct file
This commit is contained in:
Dante Catalfamo 2024-09-03 09:39:29 -04:00 committed by GitHub
parent 70a106ccb0
commit a6dcdca2dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 385 additions and 333 deletions

View file

@ -0,0 +1 @@
- Verify user has premium license before uploading VPP tokens

View file

@ -5,6 +5,7 @@ import (
"database/sql"
"errors"
"fmt"
"io"
"net/http"
"sort"
"strings"
@ -17,6 +18,9 @@ import (
"github.com/fleetdm/fleet/v4/server/mdm/apple/vpp"
)
// Used for overriding the env var value in testing
var testSetEmptyPrivateKey bool
// getVPPToken returns the base64 encoded VPP token, ready for use in requests to Apple's VPP API.
// It returns an error if the token is expired.
func (svc *Service) getVPPToken(ctx context.Context, teamID *uint) (string, error) {
@ -413,3 +417,143 @@ func getVPPAppsMetadata(ctx context.Context, ids []fleet.VPPAppTeam) ([]*fleet.V
return apps, nil
}
func (svc *Service) UploadVPPToken(ctx context.Context, token io.ReadSeeker) (*fleet.VPPTokenDB, error) {
if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionWrite); err != nil {
return nil, err
}
privateKey := svc.config.Server.PrivateKey
if testSetEmptyPrivateKey {
privateKey = ""
}
if len(privateKey) == 0 {
return nil, ctxerr.New(ctx, "Couldn't upload content token. Missing required private key. Learn how to configure the private key here: https://fleetdm.com/learn-more-about/fleet-server-private-key")
}
if token == nil {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("token", "Invalid token. Please provide a valid content token from Apple Business Manager."))
}
tokenBytes, err := io.ReadAll(token)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "reading VPP token")
}
locName, err := vpp.GetConfig(string(tokenBytes))
if err != nil {
var vppErr *vpp.ErrorResponse
if errors.As(err, &vppErr) {
// Per https://developer.apple.com/documentation/devicemanagement/app_and_book_management/app_and_book_management_legacy/interpreting_error_codes
if vppErr.ErrorNumber == 9622 {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("token", "Invalid token. Please provide a valid content token from Apple Business Manager."))
}
}
return nil, ctxerr.Wrap(ctx, err, "validating VPP token with Apple")
}
data := fleet.VPPTokenData{
Token: string(tokenBytes),
Location: locName,
}
tok, err := svc.ds.InsertVPPToken(ctx, &data)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "writing VPP token to db")
}
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), fleet.ActivityEnabledVPP{
Location: locName,
}); err != nil {
return nil, ctxerr.Wrap(ctx, err, "create activity for upload VPP token")
}
return tok, nil
}
func (svc *Service) UpdateVPPToken(ctx context.Context, tokenID uint, token io.ReadSeeker) (*fleet.VPPTokenDB, error) {
if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionWrite); err != nil {
return nil, err
}
privateKey := svc.config.Server.PrivateKey
if testSetEmptyPrivateKey {
privateKey = ""
}
if len(privateKey) == 0 {
return nil, ctxerr.New(ctx, "Couldn't upload content token. Missing required private key. Learn how to configure the private key here: https://fleetdm.com/learn-more-about/fleet-server-private-key")
}
if token == nil {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("token", "Invalid token. Please provide a valid content token from Apple Business Manager."))
}
tokenBytes, err := io.ReadAll(token)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "reading VPP token")
}
locName, err := vpp.GetConfig(string(tokenBytes))
if err != nil {
var vppErr *vpp.ErrorResponse
if errors.As(err, &vppErr) {
// Per https://developer.apple.com/documentation/devicemanagement/app_and_book_management/app_and_book_management_legacy/interpreting_error_codes
if vppErr.ErrorNumber == 9622 {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("token", "Invalid token. Please provide a valid content token from Apple Business Manager."))
}
}
return nil, ctxerr.Wrap(ctx, err, "validating VPP token with Apple")
}
data := fleet.VPPTokenData{
Token: string(tokenBytes),
Location: locName,
}
tok, err := svc.ds.UpdateVPPToken(ctx, tokenID, &data)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "updating vpp token")
}
return tok, nil
}
func (svc *Service) UpdateVPPTokenTeams(ctx context.Context, tokenID uint, teamIDs []uint) (*fleet.VPPTokenDB, error) {
if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionWrite); err != nil {
return nil, err
}
tok, err := svc.ds.UpdateVPPTokenTeams(ctx, tokenID, teamIDs)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "updating vpp token team")
}
return tok, nil
}
func (svc *Service) GetVPPTokens(ctx context.Context) ([]*fleet.VPPTokenDB, error) {
if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionRead); err != nil {
return nil, err
}
return svc.ds.ListVPPTokens(ctx)
}
func (svc *Service) DeleteVPPToken(ctx context.Context, tokenID uint) error {
if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionWrite); err != nil {
return err
}
tok, err := svc.ds.GetVPPToken(ctx, tokenID)
if err != nil {
return ctxerr.Wrap(ctx, err, "getting vpp token")
}
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), fleet.ActivityDisabledVPP{
Location: tok.Location,
}); err != nil {
return ctxerr.Wrap(ctx, err, "create activity for delete VPP token")
}
return svc.ds.DeleteVPPToken(ctx, tokenID)
}

View file

@ -30,7 +30,6 @@ import (
"github.com/fleetdm/fleet/v4/server/fleet"
"github.com/fleetdm/fleet/v4/server/mdm"
apple_mdm "github.com/fleetdm/fleet/v4/server/mdm/apple"
"github.com/fleetdm/fleet/v4/server/mdm/apple/vpp"
"github.com/fleetdm/fleet/v4/server/mdm/assets"
nanomdm "github.com/fleetdm/fleet/v4/server/mdm/nanomdm/mdm"
"github.com/fleetdm/fleet/v4/server/ptr"
@ -2542,335 +2541,3 @@ func (svc *Service) DeleteMDMAppleAPNSCert(ctx context.Context) error {
return svc.ds.SaveAppConfig(ctx, appCfg)
}
////////////////////////////////////////////////////////////////////////////////
// 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, token io.ReadSeeker) (*fleet.VPPTokenDB, error) {
if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionWrite); err != nil {
return nil, err
}
privateKey := svc.config.Server.PrivateKey
if testSetEmptyPrivateKey {
privateKey = ""
}
if len(privateKey) == 0 {
return nil, ctxerr.New(ctx, "Couldn't upload content token. Missing required private key. Learn how to configure the private key here: https://fleetdm.com/learn-more-about/fleet-server-private-key")
}
if token == nil {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("token", "Invalid token. Please provide a valid content token from Apple Business Manager."))
}
tokenBytes, err := io.ReadAll(token)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "reading VPP token")
}
locName, err := vpp.GetConfig(string(tokenBytes))
if err != nil {
var vppErr *vpp.ErrorResponse
if errors.As(err, &vppErr) {
// Per https://developer.apple.com/documentation/devicemanagement/app_and_book_management/app_and_book_management_legacy/interpreting_error_codes
if vppErr.ErrorNumber == 9622 {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("token", "Invalid token. Please provide a valid content token from Apple Business Manager."))
}
}
return nil, ctxerr.Wrap(ctx, err, "validating VPP token with Apple")
}
data := fleet.VPPTokenData{
Token: string(tokenBytes),
Location: locName,
}
tok, err := svc.ds.InsertVPPToken(ctx, &data)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "writing VPP token to db")
}
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), fleet.ActivityEnabledVPP{
Location: locName,
}); err != nil {
return nil, ctxerr.Wrap(ctx, err, "create activity for upload VPP token")
}
return tok, nil
}
////////////////////////////////////////////////////
// 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) {
if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionWrite); err != nil {
return nil, err
}
privateKey := svc.config.Server.PrivateKey
if testSetEmptyPrivateKey {
privateKey = ""
}
if len(privateKey) == 0 {
return nil, ctxerr.New(ctx, "Couldn't upload content token. Missing required private key. Learn how to configure the private key here: https://fleetdm.com/learn-more-about/fleet-server-private-key")
}
if token == nil {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("token", "Invalid token. Please provide a valid content token from Apple Business Manager."))
}
tokenBytes, err := io.ReadAll(token)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "reading VPP token")
}
locName, err := vpp.GetConfig(string(tokenBytes))
if err != nil {
var vppErr *vpp.ErrorResponse
if errors.As(err, &vppErr) {
// Per https://developer.apple.com/documentation/devicemanagement/app_and_book_management/app_and_book_management_legacy/interpreting_error_codes
if vppErr.ErrorNumber == 9622 {
return nil, ctxerr.Wrap(ctx, fleet.NewInvalidArgumentError("token", "Invalid token. Please provide a valid content token from Apple Business Manager."))
}
}
return nil, ctxerr.Wrap(ctx, err, "validating VPP token with Apple")
}
data := fleet.VPPTokenData{
Token: string(tokenBytes),
Location: locName,
}
tok, err := svc.ds.UpdateVPPToken(ctx, tokenID, &data)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "updating vpp token")
}
return tok, nil
}
////////////////////////////////////////////////////
// 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) {
if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionWrite); err != nil {
return nil, err
}
tok, err := svc.ds.UpdateVPPTokenTeams(ctx, tokenID, teamIDs)
if err != nil {
return nil, ctxerr.Wrap(ctx, err, "updating vpp token team")
}
return tok, nil
}
///////////////////////////////////////////////
// DELETE /api/_version_/fleet/vpp_tokens/%d //
///////////////////////////////////////////////
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) {
if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionRead); err != nil {
return nil, err
}
return svc.ds.ListVPPTokens(ctx)
}
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 {
if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionWrite); err != nil {
return err
}
tok, err := svc.ds.GetVPPToken(ctx, tokenID)
if err != nil {
return ctxerr.Wrap(ctx, err, "getting vpp token")
}
if err := svc.NewActivity(ctx, authz.UserFromContext(ctx), fleet.ActivityDisabledVPP{
Location: tok.Location,
}); err != nil {
return ctxerr.Wrap(ctx, err, "create activity for delete VPP token")
}
return svc.ds.DeleteVPPToken(ctx, tokenID)
}

View file

@ -2,7 +2,11 @@ package service
import (
"context"
"io"
"mime/multipart"
"net/http"
"github.com/docker/go-units"
"github.com/fleetdm/fleet/v4/server/fleet"
)
@ -73,3 +77,239 @@ func (svc *Service) AddAppStoreApp(ctx context.Context, _ *uint, _ fleet.VPPAppT
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
}