1package logical
2
3import (
4	"bytes"
5	"encoding/json"
6	"fmt"
7	"time"
8)
9
10// This logic was pulled from the http package so that it can be used for
11// encoding wrapped responses as well. It simply translates the logical
12// response to an http response, with the values we want and omitting the
13// values we don't.
14func LogicalResponseToHTTPResponse(input *Response) *HTTPResponse {
15	httpResp := &HTTPResponse{
16		Data:     input.Data,
17		Warnings: input.Warnings,
18		Headers:  input.Headers,
19	}
20
21	if input.Secret != nil {
22		httpResp.LeaseID = input.Secret.LeaseID
23		httpResp.Renewable = input.Secret.Renewable
24		httpResp.LeaseDuration = int(input.Secret.TTL.Seconds())
25	}
26
27	// If we have authentication information, then
28	// set up the result structure.
29	if input.Auth != nil {
30		httpResp.Auth = &HTTPAuth{
31			ClientToken:      input.Auth.ClientToken,
32			Accessor:         input.Auth.Accessor,
33			Policies:         input.Auth.Policies,
34			TokenPolicies:    input.Auth.TokenPolicies,
35			IdentityPolicies: input.Auth.IdentityPolicies,
36			Metadata:         input.Auth.Metadata,
37			LeaseDuration:    int(input.Auth.TTL.Seconds()),
38			Renewable:        input.Auth.Renewable,
39			EntityID:         input.Auth.EntityID,
40			TokenType:        input.Auth.TokenType.String(),
41			Orphan:           input.Auth.Orphan,
42		}
43	}
44
45	return httpResp
46}
47
48func HTTPResponseToLogicalResponse(input *HTTPResponse) *Response {
49	logicalResp := &Response{
50		Data:     input.Data,
51		Warnings: input.Warnings,
52		Headers:  input.Headers,
53	}
54
55	if input.LeaseID != "" {
56		logicalResp.Secret = &Secret{
57			LeaseID: input.LeaseID,
58		}
59		logicalResp.Secret.Renewable = input.Renewable
60		logicalResp.Secret.TTL = time.Second * time.Duration(input.LeaseDuration)
61	}
62
63	if input.Auth != nil {
64		logicalResp.Auth = &Auth{
65			ClientToken:      input.Auth.ClientToken,
66			Accessor:         input.Auth.Accessor,
67			Policies:         input.Auth.Policies,
68			TokenPolicies:    input.Auth.TokenPolicies,
69			IdentityPolicies: input.Auth.IdentityPolicies,
70			Metadata:         input.Auth.Metadata,
71			EntityID:         input.Auth.EntityID,
72			Orphan:           input.Auth.Orphan,
73		}
74		logicalResp.Auth.Renewable = input.Auth.Renewable
75		logicalResp.Auth.TTL = time.Second * time.Duration(input.Auth.LeaseDuration)
76		switch input.Auth.TokenType {
77		case "service":
78			logicalResp.Auth.TokenType = TokenTypeService
79		case "batch":
80			logicalResp.Auth.TokenType = TokenTypeBatch
81		}
82	}
83
84	return logicalResp
85}
86
87type HTTPResponse struct {
88	RequestID     string                 `json:"request_id"`
89	LeaseID       string                 `json:"lease_id"`
90	Renewable     bool                   `json:"renewable"`
91	LeaseDuration int                    `json:"lease_duration"`
92	Data          map[string]interface{} `json:"data"`
93	WrapInfo      *HTTPWrapInfo          `json:"wrap_info"`
94	Warnings      []string               `json:"warnings"`
95	Headers       map[string][]string    `json:"-"`
96	Auth          *HTTPAuth              `json:"auth"`
97}
98
99type HTTPAuth struct {
100	ClientToken      string            `json:"client_token"`
101	Accessor         string            `json:"accessor"`
102	Policies         []string          `json:"policies"`
103	TokenPolicies    []string          `json:"token_policies,omitempty"`
104	IdentityPolicies []string          `json:"identity_policies,omitempty"`
105	Metadata         map[string]string `json:"metadata"`
106	LeaseDuration    int               `json:"lease_duration"`
107	Renewable        bool              `json:"renewable"`
108	EntityID         string            `json:"entity_id"`
109	TokenType        string            `json:"token_type"`
110	Orphan           bool              `json:"orphan"`
111}
112
113type HTTPWrapInfo struct {
114	Token           string `json:"token"`
115	Accessor        string `json:"accessor"`
116	TTL             int    `json:"ttl"`
117	CreationTime    string `json:"creation_time"`
118	CreationPath    string `json:"creation_path"`
119	WrappedAccessor string `json:"wrapped_accessor,omitempty"`
120}
121
122type HTTPSysInjector struct {
123	Response *HTTPResponse
124}
125
126func (h HTTPSysInjector) MarshalJSON() ([]byte, error) {
127	j, err := json.Marshal(h.Response)
128	if err != nil {
129		return nil, err
130	}
131	// Fast path no data or empty data
132	if h.Response.Data == nil || len(h.Response.Data) == 0 {
133		return j, nil
134	}
135	// Marshaling a response will always be a JSON object, meaning it will
136	// always start with '{', so we hijack this to prepend necessary values
137	// Make a guess at the capacity, and write the object opener
138	buf := bytes.NewBuffer(make([]byte, 0, len(j)*2))
139	buf.WriteRune('{')
140	for k, v := range h.Response.Data {
141		// Marshal each key/value individually
142		mk, err := json.Marshal(k)
143		if err != nil {
144			return nil, err
145		}
146		mv, err := json.Marshal(v)
147		if err != nil {
148			return nil, err
149		}
150		// Write into the final buffer. We'll never have a valid response
151		// without any fields so we can unconditionally add a comma after each.
152		buf.WriteString(fmt.Sprintf("%s: %s, ", mk, mv))
153	}
154	// Add the rest, without the first '{'
155	buf.Write(j[1:])
156	return buf.Bytes(), nil
157}
158