From 3215dffb09838cea97abf370db608294f4e69ce3 Mon Sep 17 00:00:00 2001 From: "mend-for-github-com[bot]" <50673670+mend-for-github-com[bot]@users.noreply.github.com> Date: Mon, 19 Jun 2023 03:13:01 +0000 Subject: [PATCH 01/11] chore(deps): pin dependencies --- .github/workflows/release.yml | 6 +++--- .github/workflows/tests.yml | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cac078d..42310f4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,13 +11,13 @@ jobs: environment: release steps: - name: Set up Go 1.13 - uses: actions/setup-go@v3 + uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3 with: go-version: 1.13 id: go - name: Check out code into the Go module directory - uses: actions/checkout@v3 + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3 - name: Get dependencies run: | @@ -25,7 +25,7 @@ jobs: - name: Build run: go build -v . - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@0b7f8abb1508181956e8e162db84b466c27e18ce # v3 with: name: artifacts path: tss-sdk-go.exe diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 28a3c73..df0dc44 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -34,13 +34,13 @@ jobs: runs-on: ubuntu-latest steps: - name: Set up Go 1.13 - uses: actions/setup-go@v3 + uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3 with: go-version: 1.13 id: go - name: Check out code into the Go module directory - uses: actions/checkout@v3 + uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 # v3 - name: Get dependencies run: | From fcf81ac46f9c1724209f696a0e0ede9d567c6ed5 Mon Sep 17 00:00:00 2001 From: Tylere Zimmerman <100804646+tylerezimmerman@users.noreply.github.com> Date: Fri, 29 Sep 2023 13:03:36 -0500 Subject: [PATCH 02/11] Delete .whitesource --- .whitesource | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 .whitesource diff --git a/.whitesource b/.whitesource deleted file mode 100644 index e6def5b..0000000 --- a/.whitesource +++ /dev/null @@ -1,25 +0,0 @@ -{ - "scanSettings": { - "configMode": "AUTO", - "configExternalURL": "", - "projectToken": "861db9c562884629b71ed9255618ce0e40ccb9d3946b442bb0c6f7656082ed41", - "baseBranches": [], - "enableLicenseViolations": true, - "displayLicenseViolations": false - }, - "checkRunSettings": { - "vulnerableCheckRunConclusionLevel": "failure", - "displayMode": "diff", - "useMendCheckNames": true - }, - "issueSettings": { - "minSeverityLevel": "NONE", - "issueType": "DEPENDENCY" - }, - "remediateSettings": { - "enableRenovate": true, - "workflowRules": { - "enabled": true - } - } -} From 7a026e804bbec39b4c2679104a4f0ce48b94e5a3 Mon Sep 17 00:00:00 2001 From: Sagar Wani Date: Mon, 1 Apr 2024 07:22:58 -0400 Subject: [PATCH 03/11] added platform login support --- server/server.go | 324 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 294 insertions(+), 30 deletions(-) diff --git a/server/server.go b/server/server.go index 8ea18ac..9e3522e 100644 --- a/server/server.go +++ b/server/server.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io" + "io/ioutil" "log" "mime/multipart" "net/http" @@ -134,17 +135,17 @@ func (s Server) accessResource(method, resource, path string, input interface{}) } } - req, err := http.NewRequest(method, s.urlFor(resource, path), body) + accessToken, err := s.getAccessToken() if err != nil { - log.Printf("[ERROR] creating req: %s /%s/%s: %s", method, resource, path, err) + log.Print("[ERROR] error getting accessToken:", err) return nil, err } - accessToken, err := s.getAccessToken() + req, err := http.NewRequest(method, s.urlFor(resource, path), body) if err != nil { - log.Print("[ERROR] error getting accessToken:", err) + log.Printf("[ERROR] creating req: %s /%s/%s: %s", method, resource, path, err) return nil, err } @@ -178,17 +179,17 @@ func (s Server) searchResources(resource, searchText, field string) ([]byte, err method := "GET" body := bytes.NewBuffer([]byte{}) - req, err := http.NewRequest(method, s.urlForSearch(resource, searchText, field), body) + accessToken, err := s.getAccessToken() if err != nil { - log.Printf("[ERROR] creating req: %s /%s/%s/%s: %s", method, resource, searchText, field, err) + log.Print("[ERROR] error getting accessToken:", err) return nil, err } - accessToken, err := s.getAccessToken() + req, err := http.NewRequest(method, s.urlForSearch(resource, searchText, field), body) if err != nil { - log.Print("[ERROR] error getting accessToken:", err) + log.Printf("[ERROR] creating req: %s /%s/%s/%s: %s", method, resource, searchText, field, err) return nil, err } @@ -253,35 +254,298 @@ func (s Server) uploadFile(secretId int, fileField SecretField) error { // getAccessToken gets an OAuth2 Access Grant and returns the token // endpoint and get an accessGrant. -func (s Server) getAccessToken() (string, error) { - values := url.Values{ - "username": {s.Credentials.Username}, - "password": {s.Credentials.Password}, - "grant_type": {"password"}, +func (s *Server) getAccessToken() (string, error) { + response, err := s.checkPlatformDetails() + if err != nil { + log.Print("Error while checking server details:", err) + return "", err + } else if err == nil && response == "" { + values := url.Values{ + "username": {s.Credentials.Username}, + "password": {s.Credentials.Password}, + "grant_type": {"password"}, + } + if s.Credentials.Domain != "" { + values["domain"] = []string{s.Credentials.Domain} + } + + body := strings.NewReader(values.Encode()) + requestUrl := s.urlFor("token", "") + data, _, err := handleResponse(http.Post(requestUrl, "application/x-www-form-urlencoded", body)) + + if err != nil { + log.Print("[ERROR] grant response error:", err) + return "", err + } + + grant := struct { + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + TokenType string `json:"token_type"` + ExpiresIn int `json:"expires_in"` + }{} + + if err = json.Unmarshal(data, &grant); err != nil { + log.Print("[ERROR] parsing grant response:", err) + return "", err + } + return grant.AccessToken, nil + } else { + return response, nil } - if s.Credentials.Domain != "" { - values["domain"] = []string{s.Credentials.Domain} +} + +func (s *Server) checkPlatformDetails() (string, error) { + var baseURL string + + if s.ServerURL == "" { + baseURL = fmt.Sprintf(cloudBaseURLTemplate, s.Tenant, s.TLD) + } else { + baseURL = s.ServerURL } - body := strings.NewReader(values.Encode()) - requestUrl := s.urlFor("token", "") - data, _, err := handleResponse(http.Post(requestUrl, "application/x-www-form-urlencoded", body)) + platformHelthCheckUrl := fmt.Sprintf("%s/%s", strings.Trim(baseURL, "/"), "health") + ssHealthCheckUrl := fmt.Sprintf("%s/%s", strings.Trim(baseURL, "/"), "healthcheck.aspx") + isHealthy := checkJSONResponse(ssHealthCheckUrl) + if isHealthy { + return "", nil + } else { + isHealthy := checkJSONResponse(platformHelthCheckUrl) + if isHealthy { + requestData := map[string]string{ + "User": s.Credentials.Username, + "Version": "1.0", + } + jsonData, err := json.Marshal(requestData) + if err != nil { + log.Print("Error marshaling JSON:", err) + return "", err + } + + req, err := http.NewRequest("POST", fmt.Sprintf("%s/%s", strings.Trim(baseURL, "/"), "identity/Security/StartAuthentication"), bytes.NewBuffer(jsonData)) + if err != nil { + log.Print("Error creating HTTP request:", err) + return "", err + } + + data, _, err := handleResponse((&http.Client{}).Do(req)) + if err != nil { + log.Print("[ERROR] start authetication response error:", err) + return "", err + } + + var startAuthjsonResponse StartAuthResponse + if err = json.Unmarshal(data, &startAuthjsonResponse); err != nil { + log.Print("[ERROR] parsing start auth response:", err) + return "", err + } + + requestData = map[string]string{ + "Answer": s.Credentials.Password, + "MechanismId": findMechanismId(startAuthjsonResponse), + "Action": "Answer", + "SessionId": startAuthjsonResponse.Result.SessionId, + "TenantId": startAuthjsonResponse.Result.TenantId, + } + + jsonData, err = json.Marshal(requestData) + if err != nil { + log.Print("Error marshaling JSON:", err) + return "", err + } + + req, err = http.NewRequest("POST", fmt.Sprintf("%s/%s", strings.Trim(baseURL, "/"), "identity/Security/AdvanceAuthentication"), bytes.NewBuffer(jsonData)) + if err != nil { + log.Print("Error creating HTTP request:", err) + return "", err + } + + data, _, err = handleResponse((&http.Client{}).Do(req)) + if err != nil { + log.Print("[ERROR] advance authetication response error:", err) + return "", err + } + + var advanceAuthJsonResponse AdvanceAuthResponse + if err = json.Unmarshal(data, &advanceAuthJsonResponse); err != nil { + log.Print("[ERROR] parsing advance auth response:", err) + return "", err + } + + req, err = http.NewRequest("GET", fmt.Sprintf("%s/%s", strings.Trim(baseURL, "/"), "vaultbroker/api/vaults"), bytes.NewBuffer([]byte{})) + if err != nil { + log.Print("Error creating HTTP request:", err) + return "", err + } + req.Header.Add("Authorization", "Bearer "+advanceAuthJsonResponse.Result.OAuthTokens.AccessToken) + + data, _, err = handleResponse((&http.Client{}).Do(req)) + if err != nil { + log.Print("[ERROR] get vaults response error:", err) + return "", err + } + + var vaultJsonResponse VaultsResponseModel + if err = json.Unmarshal(data, &vaultJsonResponse); err != nil { + log.Print("[ERROR] parsing vaults response:", err) + return "", err + } + + var vaultURL string + for _, vault := range vaultJsonResponse.Vaults { + if vault.IsDefault && vault.IsActive { + vaultURL = vault.Connection.Url + break + } + } + if vaultURL != "" { + s.ServerURL = vaultURL + } + + return advanceAuthJsonResponse.Result.OAuthTokens.AccessToken, nil + } + } + return "", fmt.Errorf("invalid URL") +} + +func checkJSONResponse(url string) bool { + response, err := http.Get(url) if err != nil { - log.Print("[ERROR] grant response error:", err) - return "", err + log.Println("Error making GET request:", err) + return false } + defer response.Body.Close() - grant := struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` - TokenType string `json:"token_type"` - ExpiresIn int `json:"expires_in"` - }{} + body, err := ioutil.ReadAll(response.Body) + if err != nil { + log.Println("Error reading response body:", err) + return false + } - if err = json.Unmarshal(data, &grant); err != nil { - log.Print("[ERROR] parsing grant response:", err) - return "", err + var jsonResponse Response + err = json.Unmarshal(body, &jsonResponse) + if err == nil { + return jsonResponse.Healthy + } else { + return strings.Contains(string(body), "Healthy") + } +} + +func findMechanismId(saResponse StartAuthResponse) string { + for _, challenge := range saResponse.Result.Challenges { + for _, mechanism := range challenge.Mechanisms { + if mechanism.PromptSelectMech == "Password" { + return mechanism.MechanismId + } + } } - return grant.AccessToken, nil + return "" +} + +type Response struct { + Healthy bool `json:"healthy"` + DatabaseHealthy bool `json:"databaseHealthy"` + ServiceBusHealthy bool `json:"serviceBusHealthy"` + StorageAccountHealthy bool `json:"storageAccountHealthy"` + ScheduledForDeletion bool `json:"scheduledForDeletion"` +} + +type ClientHints struct { + PersistDefault bool `json:"PersistDefault"` + AllowPersist bool `json:"AllowPersist"` + AllowForgotPassword bool `json:"AllowForgotPassword"` + StartingPoint string `json:"StartingPoint"` + RequestedUsername string `json:"RequestedUsername"` +} + +type Mechanism struct { + AnswerType string `json:"AnswerType"` + Name string `json:"Name"` + PromptMechChosen string `json:"PromptMechChosen"` + PromptSelectMech string `json:"PromptSelectMech"` + MechanismId string `json:"MechanismId"` +} + +type Challenge struct { + Mechanisms []Mechanism `json:"Mechanisms"` +} + +type Result struct { + ClientHints ClientHints `json:"ClientHints"` + Version string `json:"Version"` + SessionId string `json:"SessionId"` + AllowLoginMfaCache bool `json:"AllowLoginMfaCache"` + Challenges []Challenge `json:"Challenges"` + Summary string `json:"Summary"` + TenantId string `json:"TenantId"` +} + +type StartAuthResponse struct { + Success bool `json:"success"` + Result Result `json:"Result"` + Message interface{} `json:"Message"` + MessageID interface{} `json:"MessageID"` + Exception interface{} `json:"Exception"` + ErrorID interface{} `json:"ErrorID"` + ErrorCode interface{} `json:"ErrorCode"` + IsSoftError bool `json:"IsSoftError"` + InnerExceptions interface{} `json:"InnerExceptions"` +} + +type OAuthTokens struct { + AccessToken string `json:"access_token"` + RefreshToken string `json:"refresh_token"` + IdToken string `json:"id_token"` + TokenType string `json:"token_type"` + ExpiresIn int `json:"expires_in"` + SessionExpiresIn int `json:"session_expires_in"` + Scope string `json:"scope"` +} + +type AdvanceAuthResult struct { + AuthLevel string `json:"AuthLevel"` + DisplayName string `json:"DisplayName"` + OAuthTokens OAuthTokens `json:"OAuthTokens"` + UserId string `json:"UserId"` + EmailAddress string `json:"EmailAddress"` + UserDirectory string `json:"UserDirectory"` + StartingPoint string `json:"StartingPoint"` + PodFqdn string `json:"PodFqdn"` + User string `json:"User"` + CustomerID string `json:"CustomerID"` + SystemID string `json:"SystemID"` + SourceDsType string `json:"SourceDsType"` + Summary string `json:"Summary"` +} + +type AdvanceAuthResponse struct { + Success bool `json:"success"` + Result AdvanceAuthResult `json:"Result"` + Message interface{} `json:"Message"` + MessageID interface{} `json:"MessageID"` + Exception interface{} `json:"Exception"` + ErrorID interface{} `json:"ErrorID"` + ErrorCode interface{} `json:"ErrorCode"` + IsSoftError bool `json:"IsSoftError"` + InnerExceptions interface{} `json:"InnerExceptions"` +} + +type Connection struct { + Url string `json:"url"` + OAuthProfileId string `json:"oAuthProfileId"` +} + +type Vault struct { + VaultId string `json:"vaultId"` + Name string `json:"name"` + Type string `json:"type"` + IsDefault bool `json:"isDefault"` + IsGlobalDefault bool `json:"isGlobalDefault"` + IsActive bool `json:"isActive"` + Connection Connection `json:"connection"` +} + +type VaultsResponseModel struct { + Vaults []Vault `json:"vaults"` } From 2ba0a9e2845072aa51271ed6ee709cd9f0ee23cc Mon Sep 17 00:00:00 2001 From: Sagar Wani Date: Tue, 2 Apr 2024 04:59:30 -0400 Subject: [PATCH 04/11] Updated README.md file --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d237d6c..100f5e2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A Golang API and examples for [Delinea](https://delinea.com/) ## Configure The API requires a `Configuration` object containing a `Username`, `Password` -and either a `Tenant` for Secret Server Cloud or a `ServerURL`: +and either a `Tenant` for Secret Server Cloud or a `ServerURL` of Secret Server/Platform: ```golang type UserCredential struct { @@ -111,10 +111,10 @@ The necessary configuration may also be configured from environment variables: | Env Var Name | Description | |----------------|------------------------------------------------------------------------------------------------------------------------------------------| -| TSS_USERNAME | The user name for the Secret Server | -| TSS_PASSWORD | The password for the user | +| TSS_USERNAME | The user name for the Secret Server/Platform | +| TSS_PASSWORD | The password for the user of Secret Server/Platform | | TSS_TENANT | Name for tenants hosted in the Secret Server Cloud. This is prepended to the *.secretservercloud.com domain to determine the server URL. | -| TSS_SERVER_URL | URL for servers not hosted in the cloud, eg: https://delinea.mycompany.com/SecretServer | +| TSS_SERVER_URL | URL for secret servers not hosted in the cloud, eg: https://delinea.mycompany.com/SecretServer or platform URL | ### Test #1 - Read Secret Password Reads the secret with ID `1` or the ID passed in the `TSS_SECRET_ID` environment variable From 6d5715cfad690962c58780fcef720f688d4f098a Mon Sep 17 00:00:00 2001 From: Sagar Wani Date: Tue, 2 Apr 2024 06:14:42 -0400 Subject: [PATCH 05/11] Returned error when configured vault not found --- server/server.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/server.go b/server/server.go index 9e3522e..ae3f187 100644 --- a/server/server.go +++ b/server/server.go @@ -401,6 +401,8 @@ func (s *Server) checkPlatformDetails() (string, error) { } if vaultURL != "" { s.ServerURL = vaultURL + } else { + return "", fmt.Errorf("no configured vault found") } return advanceAuthJsonResponse.Result.OAuthTokens.AccessToken, nil From fd3e157a4511839eeab9b0b291ec1081b2da2463 Mon Sep 17 00:00:00 2001 From: Sagar Wani Date: Wed, 3 Apr 2024 07:34:16 -0400 Subject: [PATCH 06/11] Added check for IsActive flag --- server/secret_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/secret_test.go b/server/secret_test.go index 8f28b50..ab00814 100644 --- a/server/secret_test.go +++ b/server/secret_test.go @@ -190,7 +190,7 @@ func TestSecretCRUD(t *testing.T) { // Test read of the deleted secret fails s, err := tss.Secret(sc.ID) - if s != nil { + if s != nil && s.Active { t.Errorf("deleted secret with id '%d' returned from read", sc.ID) } } @@ -559,7 +559,7 @@ func TestSecretCRUDForSSHTemplate(t *testing.T) { // Test read of the deleted secret fails s, err := tss.Secret(sc.ID) - if s != nil { + if s != nil && s.Active { t.Errorf("deleted secret with id '%d' returned from read", sc.ID) } } From e965f680e568f128570e0afe0e5ff5c1e690eb9a Mon Sep 17 00:00:00 2001 From: Tim Krehl Date: Thu, 4 Apr 2024 09:17:25 -0400 Subject: [PATCH 07/11] Update test endpoint Update the test endpoint for cloud instance instead of on-prem --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index df0dc44..6365c09 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,7 +16,7 @@ on: - main env: - TSS_SERVER_URL: ${{ secrets.TSS_SERVER_URL }} + TSS_TENANT: ${{ secrets.TSS_TENANT }} TSS_USERNAME: ${{ secrets.TSS_USERNAME }} TSS_PASSWORD: ${{ secrets.TSS_PASSWORD }} TSS_SECRET_ID: ${{ secrets.TSS_SECRET_ID }} From 66386782f140f87a48b942956558aa757e934c8a Mon Sep 17 00:00:00 2001 From: delinea-sagar <131447653+delinea-sagar@users.noreply.github.com> Date: Fri, 5 Apr 2024 02:53:39 -0400 Subject: [PATCH 08/11] Added environment variables for platform --- .github/workflows/tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6365c09..9aa1912 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,6 +27,9 @@ env: TSS_TEST_PASSWORD: ${{ secrets.TSS_TEST_PASSWORD }} TSS_SEARCH_FIELD: ${{ secrets.TSS_SEARCH_FIELD }} TSS_SEARCH_TEXT: ${{ secrets.TSS_SEARCH_TEXT }} + TSS_PLATFORM_USERNAME: ${{ secrets.TSS_PLATFORM_USERNAME }} + TSS_PLATFORM_PASSWORD: ${{ secrets.TSS_PLATFORM_PASSWORD }} + TSS_PLATFORM_SERVER_URL: ${{ secrets.TSS_PLATFORM_URL }} jobs: build: From 264ab30ca8e26988ada67a614b1692b856c46f60 Mon Sep 17 00:00:00 2001 From: delinea-sagar <131447653+delinea-sagar@users.noreply.github.com> Date: Fri, 5 Apr 2024 03:32:55 -0400 Subject: [PATCH 09/11] Added test cases for platform --- server/secret_test.go | 143 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 116 insertions(+), 27 deletions(-) diff --git a/server/secret_test.go b/server/secret_test.go index ab00814..f6185e6 100644 --- a/server/secret_test.go +++ b/server/secret_test.go @@ -11,12 +11,26 @@ import ( // TestSecret tests Secret. Referred to as "Test #1" in the README. func TestSecret(t *testing.T) { - tss, err := initServer() - if err != nil { - t.Error("configuring the Server:", err) - return - } + t.Run("SecretServer_TestSecret", func(t *testing.T) { + tss, err := initServer() + if err != nil { + t.Error("configuring the Server:", err) + return + } + GetSecret(t, tss) + }) + + t.Run("Platform_TestSecret", func(t *testing.T) { + tss, err := initPlatformServer() + if err != nil { + t.Error("configuring the Platform Server:", err) + return + } + GetSecret(t, tss) + }) +} +func GetSecret(t *testing.T, tss *Server) { id := initIntegerFromEnv("TSS_SECRET_ID", t) if id < 0 { return @@ -45,13 +59,26 @@ func TestSecret(t *testing.T) { // TestSecretCRUD tests the creation, read, update, and delete of a Secret. // Referred to as "Test #2" in the README. func TestSecretCRUD(t *testing.T) { + t.Run("SecretServer_TestSecretCRUD", func(t *testing.T) { + tss, err := initServer() + if err != nil { + t.Error("configuring the Server:", err) + return + } + SecretCRUD(t, tss) + }) - // Initialize - tss, err := initServer() - if err != nil { - t.Error("configuring the Server:", err) - return - } + t.Run("Platform_TestSecretCRUD", func(t *testing.T) { + tss, err := initPlatformServer() + if err != nil { + t.Error("configuring the Platform Server:", err) + return + } + SecretCRUD(t, tss) + }) +} + +func SecretCRUD(t *testing.T, tss *Server) { siteId := initIntegerFromEnv("TSS_SITE_ID", t) folderId := initIntegerFromEnv("TSS_FOLDER_ID", t) templateId := initIntegerFromEnv("TSS_TEMPLATE_ID", t) @@ -199,13 +226,26 @@ func TestSecretCRUD(t *testing.T) { // of a Secret which uses an SSH key template, that is, a template with extended // mappings that support SSH keys. Referred to as "Test #3" in the README. func TestSecretCRUDForSSHTemplate(t *testing.T) { + t.Run("SecretServer_TestSecretCRUDForSSHTemplate", func(t *testing.T) { + tss, err := initServer() + if err != nil { + t.Error("configuring the Server:", err) + return + } + SecretCRUDForSSHTemplate(t, tss) + }) - // Initialize - tss, err := initServer() - if err != nil { - t.Error("configuring the Server:", err) - return - } + t.Run("Platform_TestSecretCRUDForSSHTemplate", func(t *testing.T) { + tss, err := initPlatformServer() + if err != nil { + t.Error("configuring the Platform Server:", err) + return + } + SecretCRUDForSSHTemplate(t, tss) + }) +} + +func SecretCRUDForSSHTemplate(t *testing.T, tss *Server) { siteId := initIntegerFromEnv("TSS_SITE_ID", t) folderId := initIntegerFromEnv("TSS_FOLDER_ID", t) templateId := initIntegerFromEnv("TSS_SSH_KEY_TEMPLATE_ID", t) @@ -566,11 +606,26 @@ func TestSecretCRUDForSSHTemplate(t *testing.T) { // TestSearch tests Secret. Referred to as "Test #4" in the README. func TestSearch(t *testing.T) { - tss, err := initServer() - if err != nil { - t.Error("configuring the Server:", err) - return - } + t.Run("SecretServer_TestSearch", func(t *testing.T) { + tss, err := initServer() + if err != nil { + t.Error("configuring the Server:", err) + return + } + Search(t, tss) + }) + + t.Run("Platform_TestSearch", func(t *testing.T) { + tss, err := initPlatformServer() + if err != nil { + t.Error("configuring the Platform Server:", err) + return + } + Search(t, tss) + }) +} + +func Search(t *testing.T, tss *Server) { s, err := tss.Secrets(os.Getenv("TSS_SEARCH_TEXT"), os.Getenv("TSS_SEARCH_FIELD")) @@ -590,11 +645,26 @@ func TestSearch(t *testing.T) { // TestSearchWithoutField tests Secret. Referred to as "Test #5" in the README. func TestSearchWithoutField(t *testing.T) { - tss, err := initServer() - if err != nil { - t.Error("configuring the Server:", err) - return - } + t.Run("SecretServer_TestSearchWithoutField", func(t *testing.T) { + tss, err := initServer() + if err != nil { + t.Error("configuring the Server:", err) + return + } + SearchWithoutField(t, tss) + }) + + t.Run("Platform_TestSearchWithoutField", func(t *testing.T) { + tss, err := initPlatformServer() + if err != nil { + t.Error("configuring the Platform Server:", err) + return + } + SearchWithoutField(t, tss) + }) +} + +func SearchWithoutField(t *testing.T, tss *Server) { s, err := tss.Secrets(os.Getenv("TSS_SEARCH_TEXT"), "") @@ -632,6 +702,25 @@ func initServer() (*Server, error) { return New(*config) } +func initPlatformServer() (*Server, error) { + var config *Configuration + + if cj, err := ioutil.ReadFile("../test_config.json"); err == nil { + config = new(Configuration) + + json.Unmarshal(cj, &config) + } else { + config = &Configuration{ + Credentials: UserCredential{ + Username: os.Getenv("TSS_PLATFORM_USERNAME"), + Password: os.Getenv("TSS_PLATFORM_PASSWORD"), + }, + ServerURL: os.Getenv("TSS_PLATFORM_URL"), + } + } + return New(*config) +} + // initIntegerFromEnv reads the given environment variable and if it's declared, parses it to an integer. Otherwise, // returns a default integer of '1'. func initIntegerFromEnv(envVarName string, t *testing.T) int { From dcd62251d0235d206ac24f6b9d4fce37410ab343 Mon Sep 17 00:00:00 2001 From: delinea-sagar <131447653+delinea-sagar@users.noreply.github.com> Date: Fri, 5 Apr 2024 03:48:56 -0400 Subject: [PATCH 10/11] Updated variable name --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9aa1912..ebd12d5 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -29,7 +29,7 @@ env: TSS_SEARCH_TEXT: ${{ secrets.TSS_SEARCH_TEXT }} TSS_PLATFORM_USERNAME: ${{ secrets.TSS_PLATFORM_USERNAME }} TSS_PLATFORM_PASSWORD: ${{ secrets.TSS_PLATFORM_PASSWORD }} - TSS_PLATFORM_SERVER_URL: ${{ secrets.TSS_PLATFORM_URL }} + TSS_PLATFORM_URL: ${{ secrets.TSS_PLATFORM_URL }} jobs: build: From 9b50f87fb6a7f4cff8b4ccac926fda0e130def83 Mon Sep 17 00:00:00 2001 From: Sagar Wani Date: Mon, 8 Apr 2024 01:57:57 -0400 Subject: [PATCH 11/11] Updated README.md file and error message --- README.md | 23 ++++++++++++++++++++--- server/server.go | 2 +- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 100f5e2..c3e5ae2 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ type Configuration struct { ## Use -Define a `Configuration`, use it to create an instance of `Server`: +Define a `Configuration`, use it to create an instance of `Server` for Secret Server: ```golang tss := server.New(server.Configuration{ @@ -37,6 +37,20 @@ tss := server.New(server.Configuration{ }) ``` +OR + +Define a `Configuration`, use it to create an instance of `Server` for Platform: + +```golang +tss := server.New(server.Configuration{ + Credentials: UserCredential{ + Username: os.Getenv("TSS_PLATFORM_USERNAME"), + Password: os.Getenv("TSS_PLATFORM_PASSWORD"), + }, + ServerURL: os.Getenv("TSS_PLATFORM_URL"), +}) +``` + Get a secret by its numeric ID: ```golang @@ -111,10 +125,13 @@ The necessary configuration may also be configured from environment variables: | Env Var Name | Description | |----------------|------------------------------------------------------------------------------------------------------------------------------------------| -| TSS_USERNAME | The user name for the Secret Server/Platform | -| TSS_PASSWORD | The password for the user of Secret Server/Platform | +| TSS_USERNAME | The user name for the Secret Server | +| TSS_PASSWORD | The password for the user of Secret Server | | TSS_TENANT | Name for tenants hosted in the Secret Server Cloud. This is prepended to the *.secretservercloud.com domain to determine the server URL. | | TSS_SERVER_URL | URL for secret servers not hosted in the cloud, eg: https://delinea.mycompany.com/SecretServer or platform URL | +| TSS_PLATFORM_USERNAME | The user name for the Platform user | +| TSS_PLATFORM_PASSWORD | The password for the Platform user | +| TSS_PLATFORM_URL | URL for Platform, eg: https://delinea.secureplatform.com/ | ### Test #1 - Read Secret Password Reads the secret with ID `1` or the ID passed in the `TSS_SECRET_ID` environment variable diff --git a/server/server.go b/server/server.go index ae3f187..396764c 100644 --- a/server/server.go +++ b/server/server.go @@ -43,7 +43,7 @@ type Server struct { // New returns an initialized Secrets object func New(config Configuration) (*Server, error) { if config.ServerURL == "" && config.Tenant == "" || config.ServerURL != "" && config.Tenant != "" { - return nil, fmt.Errorf("either ServerURL or Tenant must be set") + return nil, fmt.Errorf("either ServerURL of Secret Server/Platform or Tenant of Secret Server Cloud must be set") } if config.TLD == "" { config.TLD = defaultTLD