| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: MPL-2.0 |
| |
| package logical |
| |
| import ( |
| "bytes" |
| "encoding/json" |
| "fmt" |
| "time" |
| ) |
| |
| // This logic was pulled from the http package so that it can be used for |
| // encoding wrapped responses as well. It simply translates the logical |
| // response to an http response, with the values we want and omitting the |
| // values we don't. |
| func LogicalResponseToHTTPResponse(input *Response) *HTTPResponse { |
| httpResp := &HTTPResponse{ |
| Data: input.Data, |
| Warnings: input.Warnings, |
| Headers: input.Headers, |
| } |
| |
| if input.Secret != nil { |
| httpResp.LeaseID = input.Secret.LeaseID |
| httpResp.Renewable = input.Secret.Renewable |
| httpResp.LeaseDuration = int(input.Secret.TTL.Seconds()) |
| } |
| |
| // If we have authentication information, then |
| // set up the result structure. |
| if input.Auth != nil { |
| httpResp.Auth = &HTTPAuth{ |
| ClientToken: input.Auth.ClientToken, |
| Accessor: input.Auth.Accessor, |
| Policies: input.Auth.Policies, |
| TokenPolicies: input.Auth.TokenPolicies, |
| IdentityPolicies: input.Auth.IdentityPolicies, |
| Metadata: input.Auth.Metadata, |
| LeaseDuration: int(input.Auth.TTL.Seconds()), |
| Renewable: input.Auth.Renewable, |
| EntityID: input.Auth.EntityID, |
| TokenType: input.Auth.TokenType.String(), |
| Orphan: input.Auth.Orphan, |
| MFARequirement: input.Auth.MFARequirement, |
| NumUses: input.Auth.NumUses, |
| } |
| } |
| |
| return httpResp |
| } |
| |
| func HTTPResponseToLogicalResponse(input *HTTPResponse) *Response { |
| logicalResp := &Response{ |
| Data: input.Data, |
| Warnings: input.Warnings, |
| Headers: input.Headers, |
| } |
| |
| if input.LeaseID != "" { |
| logicalResp.Secret = &Secret{ |
| LeaseID: input.LeaseID, |
| } |
| logicalResp.Secret.Renewable = input.Renewable |
| logicalResp.Secret.TTL = time.Second * time.Duration(input.LeaseDuration) |
| } |
| |
| if input.Auth != nil { |
| logicalResp.Auth = &Auth{ |
| ClientToken: input.Auth.ClientToken, |
| Accessor: input.Auth.Accessor, |
| Policies: input.Auth.Policies, |
| TokenPolicies: input.Auth.TokenPolicies, |
| IdentityPolicies: input.Auth.IdentityPolicies, |
| Metadata: input.Auth.Metadata, |
| EntityID: input.Auth.EntityID, |
| Orphan: input.Auth.Orphan, |
| } |
| logicalResp.Auth.Renewable = input.Auth.Renewable |
| logicalResp.Auth.TTL = time.Second * time.Duration(input.Auth.LeaseDuration) |
| switch input.Auth.TokenType { |
| case "service": |
| logicalResp.Auth.TokenType = TokenTypeService |
| case "batch": |
| logicalResp.Auth.TokenType = TokenTypeBatch |
| } |
| } |
| |
| return logicalResp |
| } |
| |
| type HTTPResponse struct { |
| RequestID string `json:"request_id"` |
| LeaseID string `json:"lease_id"` |
| Renewable bool `json:"renewable"` |
| LeaseDuration int `json:"lease_duration"` |
| Data map[string]interface{} `json:"data"` |
| WrapInfo *HTTPWrapInfo `json:"wrap_info"` |
| Warnings []string `json:"warnings"` |
| Headers map[string][]string `json:"-"` |
| Auth *HTTPAuth `json:"auth"` |
| } |
| |
| type HTTPAuth struct { |
| ClientToken string `json:"client_token"` |
| Accessor string `json:"accessor"` |
| Policies []string `json:"policies"` |
| TokenPolicies []string `json:"token_policies,omitempty"` |
| IdentityPolicies []string `json:"identity_policies,omitempty"` |
| Metadata map[string]string `json:"metadata"` |
| LeaseDuration int `json:"lease_duration"` |
| Renewable bool `json:"renewable"` |
| EntityID string `json:"entity_id"` |
| TokenType string `json:"token_type"` |
| Orphan bool `json:"orphan"` |
| MFARequirement *MFARequirement `json:"mfa_requirement"` |
| NumUses int `json:"num_uses"` |
| } |
| |
| type HTTPWrapInfo struct { |
| Token string `json:"token"` |
| Accessor string `json:"accessor"` |
| TTL int `json:"ttl"` |
| CreationTime string `json:"creation_time"` |
| CreationPath string `json:"creation_path"` |
| WrappedAccessor string `json:"wrapped_accessor,omitempty"` |
| } |
| |
| type HTTPSysInjector struct { |
| Response *HTTPResponse |
| } |
| |
| func (h HTTPSysInjector) MarshalJSON() ([]byte, error) { |
| j, err := json.Marshal(h.Response) |
| if err != nil { |
| return nil, err |
| } |
| // Fast path no data or empty data |
| if h.Response.Data == nil || len(h.Response.Data) == 0 { |
| return j, nil |
| } |
| // Marshaling a response will always be a JSON object, meaning it will |
| // always start with '{', so we hijack this to prepend necessary values |
| |
| var buf bytes.Buffer |
| buf.WriteRune('{') |
| for k, v := range h.Response.Data { |
| // Marshal each key/value individually |
| mk, err := json.Marshal(k) |
| if err != nil { |
| return nil, err |
| } |
| mv, err := json.Marshal(v) |
| if err != nil { |
| return nil, err |
| } |
| // Write into the final buffer. We'll never have a valid response |
| // without any fields so we can unconditionally add a comma after each. |
| buf.WriteString(fmt.Sprintf("%s: %s, ", mk, mv)) |
| } |
| // Add the rest, without the first '{' |
| buf.Write(j[1:]) |
| return buf.Bytes(), nil |
| } |