open-vault/sdk/logical/translate_response.go
Josh Black e83471d7de
Login MFA (#14025)
* Login MFA

* ENT OSS segragation (#14088)

* Delete method id if not used in an MFA enforcement config (#14063)

* Delete an MFA methodID only if it is not used by an MFA enforcement config

* Fixing a bug: mfa/validate is an unauthenticated path, and goes through the handleLoginRequest path

* adding use_passcode field to DUO config (#14059)

* add changelog

* preventing replay attack on MFA passcodes (#14056)

* preventing replay attack on MFA passcodes

* using %w instead of %s for error

* Improve CLI command for login mfa (#14106)

CLI prints a warning message indicating the login request needs to get validated

* adding the validity period of a passcode to error messages (#14115)

* PR feedback

* duo to handle preventing passcode reuse

Co-authored-by: hghaf099 <83242695+hghaf099@users.noreply.github.com>
Co-authored-by: hamid ghaf <hamid@hashicorp.com>
2022-02-17 13:08:51 -08:00

162 lines
5 KiB
Go

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
}