Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix(api) added configuration.apiVersion to all secret server API calls #27

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@
.vscode
.idea
test_config.json

.DS_Store
bh_example.go
9 changes: 8 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
module github.com/DelineaXPM/tss-sdk-go/v2

go 1.13
go 1.22

require github.com/tidwall/gjson v1.17.1

require (
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
)
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,4 @@ func main() {
if pw, ok := s.Field("password"); ok {
fmt.Print("the password is", pw)
}
}
}
15 changes: 8 additions & 7 deletions server/secret.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ type SshKeyArgs struct {
func (s Server) Secret(id int) (*Secret, error) {
secret := new(Secret)

if data, err := s.accessResource("GET", resource, strconv.Itoa(id), nil); err == nil {
if data, err := s.accessResource("GET", resource, strconv.Itoa(id), "v2/", nil); err == nil {
if err = json.Unmarshal(data, secret); err != nil {
log.Printf("[ERROR] error parsing response from /%s/%d: %q", resource, id, data)
return nil, err
Expand All @@ -65,7 +65,7 @@ func (s Server) Secret(id int) (*Secret, error) {
if element.IsFile && element.FileAttachmentID != 0 && element.Filename != "" {
path := fmt.Sprintf("%d/fields/%s", id, element.Slug)

if data, err := s.accessResource("GET", resource, path, nil); err == nil {
if data, err := s.accessResource("GET", resource, path, "v1/", nil); err == nil {
secret.Fields[index].ItemValue = string(data)
} else {
return nil, err
Expand All @@ -79,7 +79,7 @@ func (s Server) Secret(id int) (*Secret, error) {
// Secret gets the secret with id from the Secret Server of the given tenant
func (s Server) Secrets(searchText, field string) ([]Secret, error) {
searchResult := new(SearchResult)
if data, err := s.searchResources(resource, searchText, field); err == nil {
if data, err := s.searchResources(resource, searchText, field, "v1/"); err == nil {
if err = json.Unmarshal(data, searchResult); err != nil {
log.Printf("[ERROR] error parsing response from /%s/%s: %q", resource, searchText, data)
return nil, err
Expand Down Expand Up @@ -135,8 +135,9 @@ func (s Server) writeSecret(secret Secret, method string, path string) (*Secret,
// This SDK does support secret templates that accept both kinds
// of file fields.
fileFields := make([]SecretField, 0)
generalFields := make([]SecretField, 0)
if secret.SshKeyArgs == nil || !secret.SshKeyArgs.GenerateSshKeys {
//nolint: ineffassign
var generalFields = make([]SecretField, 0)
fileFields, generalFields, err = secret.separateFileFields(template)
if err != nil {
return nil, err
Expand All @@ -162,7 +163,7 @@ func (s Server) writeSecret(secret Secret, method string, path string) (*Secret,
secret.Fields = make([]SecretField, 0)
}

if data, err := s.accessResource(method, resource, path, secret); err == nil {
if data, err := s.accessResource(method, resource, path, "v1/", secret); err == nil {
if err = json.Unmarshal(data, writtenSecret); err != nil {
log.Printf("[ERROR] error parsing response from /%s: %q", resource, data)
return nil, err
Expand All @@ -179,7 +180,7 @@ func (s Server) writeSecret(secret Secret, method string, path string) (*Secret,
}

func (s Server) DeleteSecret(id int) error {
_, err := s.accessResource("DELETE", resource, strconv.Itoa(id), nil)
_, err := s.accessResource("DELETE", resource, strconv.Itoa(id), "v1/", nil)
return err
}

Expand Down Expand Up @@ -231,7 +232,7 @@ func (s Server) updateFiles(secretId int, fileFields []SecretField) error {
if element.ItemValue == "" {
path = fmt.Sprintf("%d/general", secretId)
input = secretPatch{Data: fieldMods{SecretFields: []fieldMod{{Slug: element.Slug, Dirty: true, Value: nil}}}}
if _, err := s.accessResource("PATCH", resource, path, input); err != nil {
if _, err := s.accessResource("PATCH", resource, path, "v2/", input); err != nil {
return err
}
} else {
Expand Down
4 changes: 2 additions & 2 deletions server/secret_template.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type SecretTemplateField struct {
func (s Server) SecretTemplate(id int) (*SecretTemplate, error) {
secretTemplate := new(SecretTemplate)

if data, err := s.accessResource("GET", templateResource, strconv.Itoa(id), nil); err == nil {
if data, err := s.accessResource("GET", templateResource, strconv.Itoa(id), "v1/", nil); err == nil {
if err = json.Unmarshal(data, secretTemplate); err != nil {
log.Printf("[ERROR] error parsing response from /%s/%d: %q", templateResource, id, data)
return nil, err
Expand All @@ -52,7 +52,7 @@ func (s Server) GeneratePassword(slug string, template *SecretTemplate) (string,
}
path := fmt.Sprintf("generate-password/%d", fieldId)

if data, err := s.accessResource("POST", templateResource, path, nil); err == nil {
if data, err := s.accessResource("POST", templateResource, path, "v1/", nil); err == nil {
passwordWithQuotes := string(data)
return passwordWithQuotes[1 : len(passwordWithQuotes)-1], nil
} else {
Expand Down
2 changes: 1 addition & 1 deletion server/secret_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func TestSecretTemplate(t *testing.T) {
return
}

if template == nil {
if template == nil || template.Fields == nil {
t.Error("secret data is nil")
}

Expand Down
60 changes: 39 additions & 21 deletions server/secret_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,21 @@ func GetSecret(t *testing.T, tss *Server) {
s, err := tss.Secret(id)

if err != nil {
t.Error("calling server.Secret:", err)
t.Errorf("calling server.Secret: err = %+v, config = %+v, secretID = %d", err, tss, id)
return
}

if s == nil {
t.Error("secret data is nil")
}

if _, ok := s.Field("password"); !ok {
t.Error("no password field")
}
if s != nil {
if _, ok := s.Field("password"); !ok {
t.Error("no password field")
}

if _, ok := s.Field("nonexistent"); ok {
t.Error("s.Field says nonexistent field exists")
if _, ok := s.Field("nonexistent"); ok {
t.Error("s.Field says nonexistent field exists")
}
} else {
t.Error("secret data is nil")
return
}
}

Expand Down Expand Up @@ -90,6 +91,8 @@ func SecretCRUD(t *testing.T, tss *Server) {
}

fieldId := -1
fieldIdUserName := -1
fieldIdMachineName := -1
if siteId < 0 || folderId < 0 || templateId < 0 {
return
}
Expand All @@ -105,6 +108,12 @@ func SecretCRUD(t *testing.T, tss *Server) {
fieldId = field.SecretTemplateFieldID
break
}
if field.DisplayName == "Machine" {
fieldIdMachineName = field.SecretTemplateFieldID
}
if field.DisplayName == "Username" {
fieldIdUserName = field.SecretTemplateFieldID
}
}
if fieldId < 0 {
t.Errorf("Unable to find a password field on the secret template with the given id '%d'", templateId)
Expand All @@ -113,15 +122,21 @@ func SecretCRUD(t *testing.T, tss *Server) {
t.Logf("Using field ID '%d' for the password field on the template with ID '%d'", fieldId, templateId)

// Test creation of a new secret
refSecret := new(Secret)
password := testPassword
refSecret.Name = "Test Secret"
refSecret := new(Secret)
refSecret.Name = "Secret Server Unit Test"
refSecret.SiteID = siteId
refSecret.FolderID = folderId
refSecret.SecretTemplateID = templateId
refSecret.Fields = make([]SecretField, 1)
refSecret.Fields[0].FieldID = fieldId
refSecret.AutoChangeEnabled = false
refSecret.Fields = make([]SecretField, 3)
refSecret.Fields[0].FieldID = fieldId // password
refSecret.Fields[0].ItemValue = password
refSecret.Fields[1].FieldID = fieldIdMachineName // machine
refSecret.Fields[1].ItemValue = "SS Test"
refSecret.Fields[2].FieldID = fieldIdUserName // username
refSecret.Fields[2].ItemValue = "ss_test_username"

sc, err := tss.CreateSecret(*refSecret)
if err != nil {
t.Error("calling server.CreateSecret:", err)
Expand Down Expand Up @@ -211,14 +226,14 @@ func SecretCRUD(t *testing.T, tss *Server) {
// Test the deletion of the new secret
err = tss.DeleteSecret(sc.ID)
if err != nil {
t.Error("calling server.DeleteSecret:", err)
t.Error("error calling server.DeleteSecret:", err)
return
}

// Test read of the deleted secret fails
s, err := tss.Secret(sc.ID)
if s != nil && s.Active {
t.Errorf("deleted secret with id '%d' returned from read", sc.ID)
if s != nil && s.Active || err != nil {
t.Errorf("deleted secret with id '%d' returned from read, err = %+v", sc.ID, err)
}
}

Expand Down Expand Up @@ -599,8 +614,8 @@ func SecretCRUDForSSHTemplate(t *testing.T, tss *Server) {

// Test read of the deleted secret fails
s, err := tss.Secret(sc.ID)
if s != nil && s.Active {
t.Errorf("deleted secret with id '%d' returned from read", sc.ID)
if s != nil && s.Active || err != nil {
t.Errorf("deleted secret with id '%d' returned from read. err = %+v", sc.ID, err)
}
}

Expand Down Expand Up @@ -687,7 +702,7 @@ func initServer() (*Server, error) {
if cj, err := ioutil.ReadFile("../test_config.json"); err == nil {
config = new(Configuration)

json.Unmarshal(cj, &config)
_ = json.Unmarshal(cj, &config)
} else {
config = &Configuration{
Credentials: UserCredential{
Expand All @@ -708,7 +723,10 @@ func initPlatformServer() (*Server, error) {
if cj, err := ioutil.ReadFile("../test_config.json"); err == nil {
config = new(Configuration)

json.Unmarshal(cj, &config)
err := json.Unmarshal(cj, &config)
if err != nil {
return nil, err
}
} else {
config = &Configuration{
Credentials: UserCredential{
Expand Down
28 changes: 15 additions & 13 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (

const (
cloudBaseURLTemplate string = "https://%s.secretservercloud.%s/"
defaultAPIPathURI string = "/api/v1"
defaultAPIPathURI string = "/api/"
defaultTokenPathURI string = "/oauth2/token"
defaultTLD string = "com"
)
Expand All @@ -30,9 +30,9 @@ type UserCredential struct {

// Configuration settings for the API
type Configuration struct {
Credentials UserCredential
ServerURL, TLD, Tenant, apiPathURI, tokenPathURI string
TLSClientConfig *tls.Config
Credentials UserCredential
ServerURL, TLD, Tenant, apiPathURI, apiVersion, tokenPathURI string
TLSClientConfig *tls.Config
}

// Server provides access to secrets stored in Delinea Secret Server
Expand Down Expand Up @@ -78,9 +78,10 @@ func (s Server) urlFor(resource, path string) string {
strings.Trim(baseURL, "/"),
strings.Trim(s.tokenPathURI, "/"))
default:
return fmt.Sprintf("%s/%s/%s/%s",
return fmt.Sprintf("%s/%s/%s/%s/%s",
strings.Trim(baseURL, "/"),
strings.Trim(s.apiPathURI, "/"),
strings.Trim(s.apiVersion, "/"),
strings.Trim(resource, "/"),
strings.Trim(path, "/"))
}
Expand All @@ -96,9 +97,10 @@ func (s Server) urlForSearch(resource, searchText, fieldName string) string {
}
switch {
case resource == "secrets":
url := fmt.Sprintf("%s/%s/%s?paging.filter.searchText=%s&paging.filter.searchField=%s&paging.filter.doNotCalculateTotal=true&paging.take=30&&paging.skip=0",
url := fmt.Sprintf("%s/%s/%s/%s?paging.filter.searchText=%s&paging.filter.searchField=%s&paging.filter.doNotCalculateTotal=true&paging.take=30&&paging.skip=0",
strings.Trim(baseURL, "/"),
strings.Trim(s.apiPathURI, "/"),
strings.Trim(s.apiVersion, "/"),
strings.Trim(resource, "/"),
searchText,
fieldName)
Expand All @@ -113,7 +115,7 @@ func (s Server) urlForSearch(resource, searchText, fieldName string) string {

// accessResource uses the accessToken to access the API resource.
// It assumes an appropriate combination of method, resource, path and input.
func (s Server) accessResource(method, resource, path string, input interface{}) ([]byte, error) {
func (s Server) accessResource(method, resource, path, version string, input interface{}) ([]byte, error) {
switch resource {
case "secrets":
case "secret-templates":
Expand All @@ -136,14 +138,13 @@ func (s Server) accessResource(method, resource, path string, input interface{})
}

accessToken, err := s.getAccessToken()

if err != nil {
log.Print("[ERROR] error getting accessToken:", err)
return nil, err
}

s.apiVersion = version
req, err := http.NewRequest(method, s.urlFor(resource, path), body)

if err != nil {
log.Printf("[ERROR] creating req: %s /%s/%s: %s", method, resource, path, err)
return nil, err
Expand All @@ -166,7 +167,7 @@ func (s Server) accessResource(method, resource, path string, input interface{})
// searchResources uses the accessToken to search for API resources.
// It assumes an appropriate combination of resource, search text.
// field is optional
func (s Server) searchResources(resource, searchText, field string) ([]byte, error) {
func (s Server) searchResources(resource, searchText, field, apiVersion string) ([]byte, error) {
switch resource {
case "secrets":
default:
Expand All @@ -176,16 +177,16 @@ func (s Server) searchResources(resource, searchText, field string) ([]byte, err
return nil, fmt.Errorf(message)
}

method := "GET"
body := bytes.NewBuffer([]byte{})

accessToken, err := s.getAccessToken()

if err != nil {
log.Print("[ERROR] error getting accessToken:", err)
return nil, err
}

method := "GET"
body := bytes.NewBuffer([]byte{})
s.apiVersion = apiVersion
req, err := http.NewRequest(method, s.urlForSearch(resource, searchText, field), body)

if err != nil {
Expand Down Expand Up @@ -239,6 +240,7 @@ func (s Server) uploadFile(secretId int, fileField SecretField) error {
return err
}

s.apiVersion = "v1/"
// Make the request
req, err := http.NewRequest("PUT", s.urlFor(resource, path), body)
if err != nil {
Expand Down
Loading