e83471d7de
* 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>
162 lines
5 KiB
Go
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
|
|
}
|