Skip to content

Commit deeb31a

Browse files
committed
feat(entry): add methods to flatten credential entry and set secret value
1 parent 735efba commit deeb31a

File tree

4 files changed

+151
-11
lines changed

4 files changed

+151
-11
lines changed

authentication.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ func NewClient(appKey string, appSecret string, baseUri string) (Client, error)
7171
}
7272

7373
func (c *Client) login() error {
74-
loginBody := fmt.Sprintf("AppKey=%s&AppSecret=%s", c.credential.appKey, c.credential.appSecret)
74+
form := url.Values{}
75+
form.Set("AppKey", c.credential.appKey)
76+
form.Set("AppSecret", c.credential.appSecret)
77+
loginBody := form.Encode()
7578

7679
reqUrl, err := url.JoinPath(c.baseUri, loginEndpoint)
7780
if err != nil {

dvls.go

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package dvls
22

33
import (
44
"encoding/json"
5+
"errors"
56
"fmt"
67
"io"
78
"net/http"
@@ -16,8 +17,10 @@ type Response struct {
1617
}
1718

1819
type RequestError struct {
19-
Url string
20-
Err error
20+
Url string
21+
StatusCode int
22+
Body []byte
23+
Err error
2124
}
2225

2326
const defaultContentType string = "application/json"
@@ -28,9 +31,23 @@ type RequestOptions struct {
2831
}
2932

3033
func (e RequestError) Error() string {
34+
if e.StatusCode != 0 {
35+
return fmt.Sprintf("error while submitting request on url %s (status %d). error: %s", e.Url, e.StatusCode, e.Err.Error())
36+
}
37+
3138
return fmt.Sprintf("error while submitting request on url %s. error: %s", e.Url, e.Err.Error())
3239
}
3340

41+
// IsNotFound reports whether the error is a DVLS RequestError with an HTTP 404 status code.
42+
func IsNotFound(err error) bool {
43+
var reqErr *RequestError
44+
if errors.As(err, &reqErr) {
45+
return reqErr.StatusCode == http.StatusNotFound
46+
}
47+
48+
return false
49+
}
50+
3451
// Request returns a Response that contains the HTTP response body in bytes, the result code and result message.
3552
func (c *Client) Request(url string, reqMethod string, reqBody io.Reader, options ...RequestOptions) (Response, error) {
3653
islogged, err := c.isLogged()
@@ -74,17 +91,19 @@ func (c *Client) rawRequest(url string, reqMethod string, contentType string, re
7491
if err != nil {
7592
return Response{}, &RequestError{Err: fmt.Errorf("error while submitting request. error: %w", err), Url: url}
7693
}
94+
defer resp.Body.Close()
7795

7896
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
79-
return Response{}, &RequestError{Err: fmt.Errorf("unexpected status code %d", resp.StatusCode), Url: url}
97+
body, _ := io.ReadAll(resp.Body)
98+
99+
return Response{}, &RequestError{Err: fmt.Errorf("unexpected status code %d", resp.StatusCode), Url: url, StatusCode: resp.StatusCode, Body: body}
80100
}
81101

82102
var response Response
83103
response.Response, err = io.ReadAll(resp.Body)
84104
if err != nil {
85105
return Response{}, &RequestError{Err: fmt.Errorf("failed to read response body. error: %w", err), Url: url}
86106
}
87-
defer resp.Body.Close()
88107

89108
if !opts.RawBody && len(response.Response) > 0 {
90109
err = json.Unmarshal(response.Response, &response)

entry_credential.go

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,117 @@ func (e *Entry) GetCredentialPrivateKeyData() (*EntryCredentialPrivateKeyData, b
109109
return data, ok
110110
}
111111

112+
// ToCredentialMap flattens a credential entry into a map of fields keyed by a stable name.
113+
// It always includes "entry-id" and "entry-name" and then subtype-specific keys.
114+
func (e Entry) ToCredentialMap() (map[string]string, error) {
115+
if e.GetType() != EntryCredentialType {
116+
return nil, fmt.Errorf("unsupported entry type (%s). Only %s is supported", e.GetType(), EntryCredentialType)
117+
}
118+
119+
secretMap := map[string]string{
120+
"entry-id": e.Id,
121+
"entry-name": e.Name,
122+
}
123+
124+
switch e.SubType {
125+
case EntryCredentialSubTypeDefault:
126+
if data, ok := e.GetCredentialDefaultData(); ok {
127+
if data.Username != "" {
128+
secretMap["username"] = data.Username
129+
}
130+
if data.Password != "" {
131+
secretMap["password"] = data.Password
132+
}
133+
if data.Domain != "" {
134+
secretMap["domain"] = data.Domain
135+
}
136+
}
137+
138+
case EntryCredentialSubTypeAccessCode:
139+
if data, ok := e.GetCredentialAccessCodeData(); ok {
140+
if data.Password != "" {
141+
secretMap["password"] = data.Password
142+
}
143+
}
144+
145+
case EntryCredentialSubTypeApiKey:
146+
if data, ok := e.GetCredentialApiKeyData(); ok {
147+
if data.ApiId != "" {
148+
secretMap["api-id"] = data.ApiId
149+
}
150+
if data.ApiKey != "" {
151+
secretMap["api-key"] = data.ApiKey
152+
}
153+
if data.TenantId != "" {
154+
secretMap["tenant-id"] = data.TenantId
155+
}
156+
}
157+
158+
case EntryCredentialSubTypeAzureServicePrincipal:
159+
if data, ok := e.GetCredentialAzureServicePrincipalData(); ok {
160+
if data.ClientId != "" {
161+
secretMap["client-id"] = data.ClientId
162+
}
163+
if data.ClientSecret != "" {
164+
secretMap["client-secret"] = data.ClientSecret
165+
}
166+
if data.TenantId != "" {
167+
secretMap["tenant-id"] = data.TenantId
168+
}
169+
}
170+
171+
case EntryCredentialSubTypeConnectionString:
172+
if data, ok := e.GetCredentialConnectionStringData(); ok {
173+
if data.ConnectionString != "" {
174+
secretMap["connection-string"] = data.ConnectionString
175+
}
176+
}
177+
178+
case EntryCredentialSubTypePrivateKey:
179+
if data, ok := e.GetCredentialPrivateKeyData(); ok {
180+
if data.Username != "" {
181+
secretMap["username"] = data.Username
182+
}
183+
if data.Password != "" {
184+
secretMap["password"] = data.Password
185+
}
186+
if data.PrivateKey != "" {
187+
secretMap["private-key"] = data.PrivateKey
188+
}
189+
if data.PublicKey != "" {
190+
secretMap["public-key"] = data.PublicKey
191+
}
192+
if data.Passphrase != "" {
193+
secretMap["passphrase"] = data.Passphrase
194+
}
195+
}
196+
197+
default:
198+
return nil, fmt.Errorf("unsupported credential subtype (%s)", e.SubType)
199+
}
200+
201+
return secretMap, nil
202+
}
203+
204+
// SetCredentialSecret mutates the Entry data to update the secret value for supported subtypes.
205+
// It preserves the existing Type/SubType but overwrites Data for the relevant subtype.
206+
func (e *Entry) SetCredentialSecret(secret string) error {
207+
if e.GetType() != EntryCredentialType {
208+
return fmt.Errorf("unsupported entry type (%s). Only %s is supported", e.GetType(), EntryCredentialType)
209+
}
210+
211+
switch e.SubType {
212+
case EntryCredentialSubTypeDefault:
213+
e.Data = &EntryCredentialDefaultData{Password: secret}
214+
case EntryCredentialSubTypeAccessCode:
215+
e.Data = &EntryCredentialAccessCodeData{Password: secret}
216+
default:
217+
return fmt.Errorf("cannot set secret for credential subtype (%s)", e.SubType)
218+
}
219+
220+
return nil
221+
}
222+
112223
// validateEntry checks if an Entry has the required fields and valid type/subtype.
113224
func (c *EntryCredentialService) validateEntry(entry *Entry) error {
114225
if entry.VaultId == "" {

server.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,22 +87,29 @@ func (z *ServerTime) UnmarshalJSON(d []byte) error {
8787
return nil
8888
}
8989

90-
dateParsed, err := time.Parse(serverTimeLayout, s)
91-
if err != nil {
92-
return err
90+
for _, layout := range serverTimeLayouts {
91+
if dateParsed, err := time.Parse(layout, s); err == nil {
92+
z.Time = dateParsed
93+
return nil
94+
}
9395
}
9496

95-
z.Time = dateParsed
96-
return nil
97+
return fmt.Errorf("cannot parse server time %q", s)
9798
}
9899

99100
const (
100101
serverPublicInfoEndpoint string = "api/public-instance-information"
101102
serverPrivateInfoEndpoint string = "api/private-instance-information"
102103
serverTimezonesEndpoint string = "/api/configuration/timezones"
103-
serverTimeLayout string = "2006-01-02T15:04:05"
104104
)
105105

106+
var serverTimeLayouts = []string{
107+
time.RFC3339Nano,
108+
"2006-01-02T15:04:05.9999999Z07:00",
109+
"2006-01-02T15:04:05.9999999Z",
110+
"2006-01-02T15:04:05",
111+
}
112+
106113
// GetPublicServerInfo returns Server that contains public information on the DVLS instance.
107114
func (c *Client) GetPublicServerInfo() (Server, error) {
108115
var server Server

0 commit comments

Comments
 (0)