From f4f247ef0612592fa7bb9fb25c2278e458bcda7e Mon Sep 17 00:00:00 2001 From: Jahziel Villasana-Espinoza Date: Fri, 24 May 2024 09:44:15 -0400 Subject: [PATCH] feat: upload apns cert --- server/fleet/service.go | 2 ++ server/service/handler.go | 5 +-- server/service/mdm.go | 74 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 3 deletions(-) diff --git a/server/fleet/service.go b/server/fleet/service.go index ff29c68a8e..5f1d17e908 100644 --- a/server/fleet/service.go +++ b/server/fleet/service.go @@ -694,6 +694,8 @@ type Service interface { // write these to the DB. On subsequent calls, it will use the saved APNS key for generating the CSR. GetMDMAppleCSR(ctx context.Context) ([]byte, error) + UploadMDMAppleAPNSCert(ctx context.Context, cert io.ReadSeeker) error + // GetHostDEPAssignment retrieves the host DEP assignment for the specified host. GetHostDEPAssignment(ctx context.Context, host *Host) (*HostDEPAssignment, error) diff --git a/server/service/handler.go b/server/service/handler.go index 825aafa7f1..0c26a23d96 100644 --- a/server/service/handler.go +++ b/server/service/handler.go @@ -495,8 +495,6 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC // Generative AI ue.POST("/api/_version_/fleet/autofill/policy", autofillPoliciesEndpoint, autofillPoliciesRequest{}) - ue.GET("/api/_version_/fleet/mdm/apple/request_csr", getMDMAppleCSREndpoint, getMDMAppleCSRRequest{}) - // Only Fleet MDM specific endpoints should be within the root /mdm/ path. // NOTE: remember to update // `service.mdmConfigurationRequiredEndpoints` when you add an @@ -714,6 +712,9 @@ func attachFleetAPIRoutes(r *mux.Router, svc fleet.Service, config config.FleetC ue.POST("/api/_version_/fleet/mdm/apple/request_csr", requestMDMAppleCSREndpoint, requestMDMAppleCSRRequest{}) ue.POST("/api/_version_/fleet/mdm/apple/dep/key_pair", newMDMAppleDEPKeyPairEndpoint, nil) + ue.GET("/api/_version_/fleet/mdm/apple/request_csr", getMDMAppleCSREndpoint, getMDMAppleCSRRequest{}) + ue.POST("/api/_version_/fleet/mdm/apple/apns_certificate", uploadMDMAppleAPNSCertEndpoint, uploadMDMAppleAPNSCertRequest{}) + // Deprecated: GET /mdm/apple_bm is now deprecated, replaced by the // GET /abm endpoint. ue.GET("/api/_version_/fleet/mdm/apple_bm", getAppleBMEndpoint, nil) diff --git a/server/service/mdm.go b/server/service/mdm.go index b73fc931de..343fdd3846 100644 --- a/server/service/mdm.go +++ b/server/service/mdm.go @@ -2221,6 +2221,78 @@ func (svc *Service) GetMDMAppleCSR(ctx context.Context) ([]byte, error) { return nil, ctxerr.Wrap(ctx, err, "get signed CSR") } - // Return signed CSR; these bytes are already base64 encoded + // Return signed CSR return signedCSRB64, nil } + +type uploadMDMAppleAPNSCertRequest struct { + File *multipart.FileHeader +} + +func (uploadMDMAppleAPNSCertRequest) DecodeRequest(ctx context.Context, r *http.Request) (interface{}, error) { + decoded := uploadSoftwareInstallerRequest{} + 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["certificate"] == nil || len(r.MultipartForm.File["certificate"]) == 0 { + return nil, &fleet.BadRequestError{ + Message: "certificate multipart field is required", + InternalErr: err, + } + } + + decoded.File = r.MultipartForm.File["certificate"][0] + + return &decoded, nil +} + +type uploadMDMAppleAPNSCertResponse struct { + Err error `json:"error,omitempty"` +} + +func (r uploadMDMAppleAPNSCertResponse) error() error { + return r.Err +} + +func (r uploadMDMAppleAPNSCertResponse) Status() int { return http.StatusAccepted } + +func uploadMDMAppleAPNSCertEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) { + req := request.(*uploadSoftwareInstallerRequest) + file, err := req.File.Open() + if err != nil { + return uploadMDMAppleAPNSCertResponse{Err: err}, nil + } + defer file.Close() + + if err := svc.UploadMDMAppleAPNSCert(ctx, file); err != nil { + return &uploadMDMAppleAPNSCertResponse{Err: err}, nil + } + + return &uploadMDMAppleAPNSCertResponse{}, nil +} + +func (svc *Service) UploadMDMAppleAPNSCert(ctx context.Context, cert io.ReadSeeker) error { + if err := svc.authz.Authorize(ctx, &fleet.AppleCSR{}, fleet.ActionWrite); err != nil { + return ctxerr.Wrap(ctx, err) + } + + // Get cert file bytes + certBytes, err := io.ReadAll(cert) + if err != nil { + return ctxerr.Wrap(ctx, err, "reading apns certificate") + } + + // Save to DB + if err := svc.ds.InsertMDMConfigAssets(ctx, []fleet.MDMConfigAsset{ + {Name: fleet.MDMAssetAPNSCert, Value: certBytes}, + }); err != nil { + return ctxerr.Wrap(ctx, err, "writing apns cert to db") + } + + return nil +}