package msgdata import ( "encoding/json" "errors" "fmt" "edge-infra.dev/pkg/sds/emergencyaccess/apierror" "edge-infra.dev/pkg/sds/emergencyaccess/eaconst" ) type v2_0CommandRequest struct { ReqAttr map[string]string `json:"attributes"` ReqData v2Command `json:"data"` } // Create a structured v2.0 request from a full command string func NewV2_0Request(payload string) (req Request, err error) { requestType := determineRequestType(payload) name, args, err := parsePayload(payload) if err != nil { return nil, apierror.E(apierror.ErrInvalidCommand, err) } attr := map[string]string{ eaconst.VersionKey: string(eaconst.MessageVersion2_0), eaconst.RequestTypeKey: string(requestType), } switch requestType { case eaconst.Command: r := v2_0CommandRequest{ ReqData: v2Command{ Command: name, Args: args, }, ReqAttr: attr, } if err := r.validate(); err != nil { return nil, err } req = r case eaconst.Executable: r := v2_0ExecutableRequest{ ReqData: v2Executable{ Executable: executable{ Name: stripExecutablePrefix(name), // Contents not expected to be populated at this point }, Args: args, }, ReqAttr: attr, } if err := r.validate(); err != nil { return nil, err } req = &r default: return nil, fmt.Errorf("request type %q not supported", requestType) } return req, nil } type v2Command struct { Command string `json:"command"` Args []string `json:"args"` } func (v v2_0CommandRequest) AddAttribute(key, val string) { if _, ok := v.ReqAttr[key]; !ok { v.ReqAttr[key] = val } } func (v v2_0CommandRequest) Attributes() map[string]string { return deepCopyMap(v.ReqAttr) } func (v v2_0CommandRequest) CommandToBeAuthorized() string { return v.ReqData.Command } func (v v2_0CommandRequest) Data() ([]byte, error) { return json.Marshal(v.ReqData) } func (v v2_0CommandRequest) RequestType() eaconst.RequestType { return eaconst.Command } func (v v2_0CommandRequest) validate() error { var err error var apiErr error if v.ReqData.Command == "" { apiErr = apierror.E(apierror.ErrInvalidCommand, "No command identified") err = errors.Join(err, errors.New("command is empty")) } if attrErr := validateAttributes(v.ReqAttr, string(eaconst.MessageVersion2_0), string(eaconst.Command)); attrErr != nil { err = errors.Join(err, attrErr) } if err != nil { return apierror.E(apierror.ErrInvalidCommand, apiErr, err) } return nil } func assembleV2_0CommandRequest(data []byte, attributes map[string]string) (Request, error) { var reqData v2Command if err := json.Unmarshal(data, &reqData); err != nil { return nil, fmt.Errorf("failed to unmarshal data: %w", err) } req := v2_0CommandRequest{ ReqData: reqData, ReqAttr: deepCopyMap(attributes), } if err := req.validate(); err != nil { return nil, err } return req, nil } type v2_0ExecutableRequest struct { ReqAttr map[string]string `json:"attributes"` ReqData v2Executable `json:"data"` } type v2Executable struct { Executable executable `json:"executable"` Args []string `json:"args"` } type executable struct { Name string `json:"name"` Contents string `json:"contents"` } func (v *v2_0ExecutableRequest) AddAttribute(key, val string) { if _, ok := v.ReqAttr[key]; !ok { v.ReqAttr[key] = val } } func (v *v2_0ExecutableRequest) Attributes() map[string]string { return deepCopyMap(v.ReqAttr) } func (v *v2_0ExecutableRequest) CommandToBeAuthorized() string { return v.ReqData.Executable.Name } func (v *v2_0ExecutableRequest) Data() ([]byte, error) { return json.Marshal(v.ReqData) } func (v *v2_0ExecutableRequest) RequestType() eaconst.RequestType { return eaconst.Executable } func (v *v2_0ExecutableRequest) WriteContents(contents string) { v.ReqData.Executable.Contents = contents } func (v v2_0ExecutableRequest) validate() error { var err error var apiErr error if v.ReqData.Executable.Name == "" { apiErr = apierror.E(apierror.ErrInvalidCommand, "No executable name identified") err = errors.Join(err, errors.New("executable name is empty")) } if attrErr := validateAttributes(v.ReqAttr, string(eaconst.MessageVersion2_0), string(eaconst.Executable)); attrErr != nil { err = errors.Join(err, attrErr) } if err != nil { return apierror.E(apierror.ErrInvalidCommand, apiErr, err) } return nil } func assembleV2_0ExecutableRequest(data []byte, attributes map[string]string) (Request, error) { var reqData v2Executable if err := json.Unmarshal(data, &reqData); err != nil { return nil, fmt.Errorf("failed to unmarshal data: %w", err) } req := v2_0ExecutableRequest{ ReqData: reqData, ReqAttr: deepCopyMap(attributes), } if err := req.validate(); err != nil { return nil, err } return &req, nil }