-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore!: added different client services to improve usage
- Loading branch information
1 parent
1059b5d
commit f72b48a
Showing
6 changed files
with
421 additions
and
391 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,356 +1,5 @@ | ||
package dvls | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
// Entry represents a DVLS entry/connection. | ||
type Entry struct { | ||
ID string `json:"id,omitempty"` | ||
VaultId string `json:"repositoryId"` | ||
EntryName string `json:"name"` | ||
Description string `json:"description"` | ||
EntryFolderPath string `json:"group"` | ||
ModifiedDate *ServerTime `json:"modifiedDate,omitempty"` | ||
ConnectionType ServerConnectionType `json:"connectionType"` | ||
ConnectionSubType ServerConnectionSubType `json:"connectionSubType"` | ||
Tags []string `json:"keywords,omitempty"` | ||
|
||
Credentials EntryCredentials `json:"data,omitempty"` | ||
} | ||
|
||
// MarshalJSON implements the json.Marshaler interface. | ||
func (e Entry) MarshalJSON() ([]byte, error) { | ||
raw := struct { | ||
Id string `json:"id,omitempty"` | ||
RepositoryId string `json:"repositoryId"` | ||
Name string `json:"name"` | ||
Description string `json:"description"` | ||
Events struct { | ||
OpenCommentPrompt bool `json:"openCommentPrompt"` | ||
CredentialViewedPrompt bool `json:"credentialViewedPrompt"` | ||
TicketNumberIsRequiredOnCredentialViewed bool `json:"ticketNumberIsRequiredOnCredentialViewed"` | ||
TicketNumberIsRequiredOnClose bool `json:"ticketNumberIsRequiredOnClose"` | ||
CredentialViewedCommentIsRequired bool `json:"credentialViewedCommentIsRequired"` | ||
TicketNumberIsRequiredOnOpen bool `json:"ticketNumberIsRequiredOnOpen"` | ||
CloseCommentIsRequired bool `json:"closeCommentIsRequired"` | ||
OpenCommentPromptOnBrowserExtensionLink bool `json:"openCommentPromptOnBrowserExtensionLink"` | ||
CloseCommentPrompt bool `json:"closeCommentPrompt"` | ||
OpenCommentIsRequired bool `json:"openCommentIsRequired"` | ||
WarnIfAlreadyOpened bool `json:"warnIfAlreadyOpened"` | ||
} `json:"events"` | ||
Data string `json:"data"` | ||
Expiration string `json:"expiration"` | ||
CheckOutMode int `json:"checkOutMode"` | ||
Group string `json:"group"` | ||
ConnectionType ServerConnectionType `json:"connectionType"` | ||
ConnectionSubType ServerConnectionSubType `json:"connectionSubType"` | ||
Keywords string `json:"keywords"` | ||
}{} | ||
|
||
for i, v := range e.Tags { | ||
if strings.Contains(v, " ") { | ||
e.Tags[i] = "\"" + v + "\"" | ||
} | ||
} | ||
|
||
raw.Id = e.ID | ||
raw.Keywords = strings.Join(e.Tags, " ") | ||
raw.Description = e.Description | ||
raw.RepositoryId = e.VaultId | ||
raw.Group = e.EntryFolderPath | ||
raw.ConnectionSubType = e.ConnectionSubType | ||
raw.ConnectionType = e.ConnectionType | ||
raw.Name = e.EntryName | ||
sensitiveJson, err := json.Marshal(e.Credentials) | ||
if err != nil { | ||
return nil, fmt.Errorf("failed to marshall sensitive data. error: %w", err) | ||
} | ||
|
||
raw.Data = string(sensitiveJson) | ||
|
||
entryJson, err := json.Marshal(raw) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return entryJson, nil | ||
} | ||
|
||
// UnmarshalJSON implements the json.Unmarshaler interface. | ||
func (e *Entry) UnmarshalJSON(d []byte) error { | ||
raw := struct { | ||
Data struct { | ||
ID string | ||
Description string | ||
Name string | ||
Group string | ||
Username string | ||
ModifiedDate *ServerTime | ||
Keywords string | ||
RepositoryId string | ||
ConnectionType ServerConnectionType | ||
ConnectionSubType ServerConnectionSubType | ||
} | ||
}{} | ||
err := json.Unmarshal(d, &raw) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
e.ID = raw.Data.ID | ||
e.EntryName = raw.Data.Name | ||
e.ConnectionType = raw.Data.ConnectionType | ||
e.ConnectionSubType = raw.Data.ConnectionSubType | ||
e.ModifiedDate = raw.Data.ModifiedDate | ||
e.Credentials.Username = raw.Data.Username | ||
e.Description = raw.Data.Description | ||
e.EntryFolderPath = raw.Data.Group | ||
e.VaultId = raw.Data.RepositoryId | ||
|
||
var spacedTag bool | ||
tags := strings.FieldsFunc(raw.Data.Keywords, func(r rune) bool { | ||
if r == '"' { | ||
spacedTag = !spacedTag | ||
} | ||
return !spacedTag && r == ' ' | ||
}) | ||
for i, v := range tags { | ||
unquotedTag, err := strconv.Unquote(v) | ||
if err != nil { | ||
continue | ||
} | ||
|
||
tags[i] = unquotedTag | ||
} | ||
|
||
e.Tags = tags | ||
|
||
return nil | ||
} | ||
|
||
// EntryCredentials represents an Entry Credentials fields. | ||
type EntryCredentials struct { | ||
Username string | ||
Password *string | ||
} | ||
|
||
// MarshalJSON implements the json.Marshaler interface. | ||
func (s EntryCredentials) MarshalJSON() ([]byte, error) { | ||
raw := struct { | ||
AllowClipboard bool `json:"allowClipboard"` | ||
CredentialConnectionId string `json:"credentialConnectionId"` | ||
PamCredentialId string `json:"pamCredentialId"` | ||
PamCredentialName string `json:"pamCredentialName"` | ||
CredentialMode int `json:"credentialMode"` | ||
Credentials *string `json:"credentials"` | ||
Domain string `json:"domain"` | ||
MnemonicPassword string `json:"mnemonicPassword"` | ||
PasswordItem struct { | ||
HasSensitiveData bool `json:"hasSensitiveData"` | ||
SensitiveData string `json:"sensitiveData"` | ||
} `json:"passwordItem"` | ||
PromptForPassword bool `json:"promptForPassword"` | ||
UserName string `json:"userName"` | ||
}{} | ||
|
||
if s.Password != nil { | ||
raw.PasswordItem.HasSensitiveData = true | ||
raw.PasswordItem.SensitiveData = *s.Password | ||
} | ||
raw.UserName = s.Username | ||
|
||
secretJson, err := json.Marshal(raw) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return secretJson, nil | ||
} | ||
|
||
// UnmarshalJSON implements the json.Unmarshaler interface. | ||
func (s *EntryCredentials) UnmarshalJSON(d []byte) error { | ||
raw := struct { | ||
Data string | ||
}{} | ||
err := json.Unmarshal(d, &raw) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if raw.Data != "" { | ||
newRaw := struct { | ||
Data struct { | ||
Credentials struct { | ||
Username string | ||
Password string | ||
} | ||
} | ||
}{} | ||
err = json.Unmarshal([]byte(raw.Data), &newRaw) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
s.Username = newRaw.Data.Credentials.Username | ||
s.Password = &newRaw.Data.Credentials.Password | ||
} | ||
|
||
return nil | ||
} | ||
|
||
const ( | ||
entryEndpoint string = "/api/connections/partial" | ||
) | ||
|
||
// GetEntryCredentialsPassword returns entry with the entry.Credentials.Password field. | ||
func (c *Client) GetEntryCredentialsPassword(entry Entry) (Entry, error) { | ||
var secret EntryCredentials | ||
reqUrl, err := url.JoinPath(c.baseUri, entryEndpoint, entry.ID, "/sensitive-data") | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("failed to build entry url. error: %w", err) | ||
} | ||
|
||
resp, err := c.Request(reqUrl, http.MethodPost, nil) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("error while fetching sensitive data. error: %w", err) | ||
} else if err = resp.CheckRespSaveResult(); err != nil { | ||
return Entry{}, err | ||
} | ||
|
||
err = json.Unmarshal(resp.Response, &secret) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("failed to unmarshall response body. error: %w", err) | ||
} | ||
|
||
entry.Credentials = secret | ||
|
||
return entry, nil | ||
} | ||
|
||
// GetEntry returns a single Entry specified by entryId. Call GetEntryCredentialsPassword with | ||
// the returned Entry to fetch the password. | ||
func (c *Client) GetEntry(entryId string) (Entry, error) { | ||
var entry Entry | ||
reqUrl, err := url.JoinPath(c.baseUri, entryEndpoint, entryId) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("failed to build entry url. error: %w", err) | ||
} | ||
|
||
resp, err := c.Request(reqUrl, http.MethodGet, nil) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("error while fetching entry. error: %w", err) | ||
} else if err = resp.CheckRespSaveResult(); err != nil { | ||
return Entry{}, err | ||
} | ||
|
||
err = json.Unmarshal(resp.Response, &entry) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("failed to unmarshall response body. error: %w", err) | ||
} | ||
|
||
return entry, nil | ||
} | ||
|
||
// NewEntry creates a new Entry based on entry. | ||
func (c *Client) NewEntry(entry Entry) (Entry, error) { | ||
if entry.ConnectionType != ServerConnectionCredential || entry.ConnectionSubType != ServerConnectionSubTypeDefault { | ||
return Entry{}, fmt.Errorf("unsupported entry type (%s %s). Only %s %s is supported", entry.ConnectionType, entry.ConnectionSubType, ServerConnectionCredential, ServerConnectionSubTypeDefault) | ||
} | ||
|
||
entry.ID = "" | ||
entry.ModifiedDate = nil | ||
|
||
reqUrl, err := url.JoinPath(c.baseUri, entryEndpoint, "save") | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("failed to build entry url. error: %w", err) | ||
} | ||
|
||
entryJson, err := json.Marshal(entry) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("failed to marshall body. error: %w", err) | ||
} | ||
|
||
resp, err := c.Request(reqUrl, http.MethodPost, bytes.NewBuffer(entryJson)) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("error while creating entry. error: %w", err) | ||
} else if err = resp.CheckRespSaveResult(); err != nil { | ||
return Entry{}, err | ||
} | ||
|
||
err = json.Unmarshal(resp.Response, &entry) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("failed to unmarshall response body. error: %w", err) | ||
} | ||
|
||
return entry, nil | ||
} | ||
|
||
// UpdateEntry updates an Entry based on entry. Will replace all other fields wether included or not. | ||
func (c *Client) UpdateEntry(entry Entry) (Entry, error) { | ||
if entry.ConnectionType != ServerConnectionCredential || entry.ConnectionSubType != ServerConnectionSubTypeDefault { | ||
return Entry{}, fmt.Errorf("unsupported entry type (%s %s). Only %s %s is supported", entry.ConnectionType, entry.ConnectionSubType, ServerConnectionCredential, ServerConnectionSubTypeDefault) | ||
} | ||
_, err := c.GetEntry(entry.ID) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("error while fetching entry. error: %w", err) | ||
} | ||
|
||
entry.ModifiedDate = nil | ||
|
||
reqUrl, err := url.JoinPath(c.baseUri, entryEndpoint, "save") | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("failed to build entry url. error: %w", err) | ||
} | ||
|
||
entryJson, err := json.Marshal(entry) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("failed to marshall body. error: %w", err) | ||
} | ||
|
||
resp, err := c.Request(reqUrl, http.MethodPut, bytes.NewBuffer(entryJson)) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("error while creating entry. error: %w", err) | ||
} else if err = resp.CheckRespSaveResult(); err != nil { | ||
return Entry{}, err | ||
} | ||
|
||
err = json.Unmarshal(resp.Response, &entry) | ||
if err != nil { | ||
return Entry{}, fmt.Errorf("failed to unmarshall response body. error: %w", err) | ||
} | ||
|
||
return entry, nil | ||
} | ||
|
||
// DeleteEntry deletes an entry based on entryId. | ||
func (c *Client) DeleteEntry(entryId string) error { | ||
reqUrl, err := url.JoinPath(c.baseUri, entryEndpoint, entryId) | ||
if err != nil { | ||
return fmt.Errorf("failed to delete entry url. error: %w", err) | ||
} | ||
|
||
resp, err := c.Request(reqUrl, http.MethodDelete, nil) | ||
if err != nil { | ||
return fmt.Errorf("error while deleting entry. error: %w", err) | ||
} else if err = resp.CheckRespSaveResult(); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
// NewEntryCredentials returns an EntryCredentials with an initialised EntryCredentials.Password. | ||
func NewEntryCredentials(username string, password string) EntryCredentials { | ||
creds := EntryCredentials{ | ||
Username: username, | ||
Password: &password, | ||
} | ||
return creds | ||
type Entries struct { | ||
UserCredential *EntryUserCredentialService | ||
} |
Oops, something went wrong.