From 38f6f9e2c15bf49ea9e08c12c6d9e339ad027878 Mon Sep 17 00:00:00 2001 From: Ilja <47740286+iljaSL@users.noreply.github.com> Date: Thu, 20 Jun 2024 12:29:11 +0300 Subject: [PATCH] feat: add new api endpoints, remove third party packages usage (#149) * feat: add new api endpoints, remove third party packages usage --- api/authorizer/client.go | 100 ++++++-- api/authorizer/model.go | 136 +++++++++-- api/secretsmanager/client.go | 455 +++++++++++++++++++++++++++++++++++ api/secretsmanager/model.go | 277 +++++++++++++++++++++ go.mod | 12 +- go.sum | 12 - pkce/pkce_test.go | 14 +- restapi/client.go | 26 +- restapi/errors_test.go | 23 +- 9 files changed, 977 insertions(+), 78 deletions(-) create mode 100644 api/secretsmanager/client.go create mode 100644 api/secretsmanager/model.go diff --git a/api/authorizer/client.go b/api/authorizer/client.go index f7bc6f2..5ad0c49 100644 --- a/api/authorizer/client.go +++ b/api/authorizer/client.go @@ -17,21 +17,6 @@ type Client struct { api restapi.Connector } -type templatesResult struct { - Count int `json:"count"` - Items []CertTemplate `json:"items"` -} - -type accessGroupResult struct { - Count int `json:"count"` - Items []AccessGroup `json:"items"` -} - -type apiCertificateResult struct { - Count int `json:"count"` - Items []APICertificate `json:"items"` -} - // New creates a new authorizer client instance func New(api restapi.Connector) *Client { return &Client{api: api} @@ -355,6 +340,7 @@ func (auth *Client) ExtenderTrustAnchor() (*TrustAnchor, error) { return anchor, err } +// MARK: Access Groups // AccessGroups lists all access group func (auth *Client) AccessGroups(offset, limit int, sortkey, sortdir string) ([]AccessGroup, error) { filters := Params{ @@ -453,6 +439,7 @@ func (auth *Client) DeleteAccessGroupsIdCas(accessGroupID string, caID string) e return err } +// MARK: Certs // SearchCert search for certificates func (auth *Client) SearchCert(offset, limit int, sortkey, sortdir string, cert *APICertificateSearch) ([]APICertificate, error) { filters := Params{ @@ -492,3 +479,86 @@ func (auth *Client) GetCertByID(ID string) (ApiCertificateObject, error) { return cert, err } + +// MARK: Secrets +// AccountSecrets lists all account secrets +func (auth *Client) AccountSecrets(limit int, sortdir string) (AccountSecretsResult, error) { + filters := Params{ + Limit: limit, + Sortdir: sortdir, + } + result := AccountSecretsResult{} + + _, err := auth.api. + URL("/authorizer/api/v1/secrets"). + Query(&filters). + Get(&result) + + return result, err +} + +// SearchAccountSecrets search for account secrets +func (auth *Client) SearchAccountSecrets(limit int, sortdir string, search *AccountSecretsSearchRequest) (AccountSecretsResult, error) { + filters := Params{ + Limit: limit, + Sortdir: sortdir, + } + result := AccountSecretsResult{} + + _, err := auth.api. + URL("/authorizer/api/v1/secrets/search"). + Query(&filters). + Post(search, &result) + + return result, err +} + +// CheckoutAccountSecret checkout account secret +func (auth *Client) CheckoutAccountSecret(path string) (CheckoutResult, error) { + checkoutReq := CheckoutRequest{ + Path: path, + } + result := CheckoutResult{} + + _, err := auth.api. + URL("/authorizer/api/v1/secrets/checkouts"). + Post(checkoutReq, &result) + + return result, err +} + +// Checkouts lists secret checkouts +func (auth *Client) Checkouts(limit int, sortdir string) (CheckoutResult, error) { + filters := Params{ + Limit: limit, + Sortdir: sortdir, + } + result := CheckoutResult{} + + _, err := auth.api. + URL("/authorizer/api/v1/secrets/checkouts"). + Query(&filters). + Get(&result) + + return result, err +} + +// Checkout get checkout by id +func (auth *Client) Checkout(checkoutId string) (*Checkout, error) { + checkout := &Checkout{} + + _, err := auth.api. + URL("/authorizer/api/v1/secrets/checkouts/%s", url.PathEscape(checkoutId)). + Get(&checkout) + + return checkout, err +} + +// ReleaseCheckout release secret checkout +func (auth *Client) ReleaseCheckout(checkoutId string) error { + _, err := auth.api. + URL("/authorizer/api/v1/secrets/checkouts/%s/release", url.PathEscape(checkoutId)). + Post(nil) + + return err +} diff --git a/api/authorizer/model.go b/api/authorizer/model.go index 72de4f4..c2abcd1 100644 --- a/api/authorizer/model.go +++ b/api/authorizer/model.go @@ -6,6 +6,8 @@ package authorizer +import "time" + // Params query params definition type Params struct { ResponseType string `json:"response_type,omitempty"` @@ -158,26 +160,114 @@ type ApiCertificateSearchResponse struct { } type ApiCertificateObject struct { - Type string `json:"type"` - ID string `json:"id"` - Serial string `json:"serial"` - OwnerID string `json:"owner_id,omitempty"` - Revoked string `json:"revoked,omitempty"` - RevocationReason string `json:"revocation_reason,omitempty"` - Cert string `json:"cert"` - Chain string `json:"chain"` - Issuer string `json:"issuer,omitempty"` - Subject string `json:"subject,omitempty"` - NotBefore string `json:"not_before,omitempty"` - NotAfter string `json:"not_after,omitempty"` - KeyUsage string `json:"key_usage,omitempty"` - BasicConstraints string `json:"basic_constraints,omitempty"` - Extensions string `json:"extensions,omitempty"` - FingerPrintSHA1 string `json:"fingerprint_sha1,omitempty"` - FingerPrintSHA256 string `json:"fingerprint_sha256,omitempty"` - SubjectKeyID string `json:"subject_key_id,omitempty"` - AuthorityKeyID string `json:"authority_key_id,omitempty"` - ExpiryStatus ExpiryStatus `json:"expiry_status,omitempty"` -} -// ExpiryStatus specifies the certificate expiry status -type ExpiryStatus string \ No newline at end of file + Type string `json:"type"` + ID string `json:"id"` + Serial string `json:"serial"` + OwnerID string `json:"owner_id,omitempty"` + Revoked string `json:"revoked,omitempty"` + RevocationReason string `json:"revocation_reason,omitempty"` + Cert string `json:"cert"` + Chain string `json:"chain"` + Issuer string `json:"issuer,omitempty"` + Subject string `json:"subject,omitempty"` + NotBefore string `json:"not_before,omitempty"` + NotAfter string `json:"not_after,omitempty"` + KeyUsage string `json:"key_usage,omitempty"` + BasicConstraints string `json:"basic_constraints,omitempty"` + Extensions string `json:"extensions,omitempty"` + FingerPrintSHA1 string `json:"fingerprint_sha1,omitempty"` + FingerPrintSHA256 string `json:"fingerprint_sha256,omitempty"` + SubjectKeyID string `json:"subject_key_id,omitempty"` + AuthorityKeyID string `json:"authority_key_id,omitempty"` + ExpiryStatus string `json:"expiry_status,omitempty"` +} + +type AccountSecrets struct { + Path string `json:"path"` + Type string `json:"type"` + Username string `json:"username"` + Email string `json:"email,omitempty"` + FullName string `json:"full_name,omitempty"` + TargetDomain TargetDomainHandle `json:"target_domain,omitempty"` + Host HostPrincipals `json:"host,omitempty"` + Created string `json:"created,omitempty"` + Updated string `json:"updated,omitempty"` +} + +type TargetDomainHandle struct { + ID string `json:"id"` + Name string `json:"name,omitempty"` + Deleted bool `json:"deleted,omitempty"` +} + +type HostPrincipals struct { + ID string `json:"id"` + Addresses []string `json:"addresses"` + CommonName string `json:"common_name,omitempty"` + ExternalID string `json:"external_id,omitempty"` + InstanceID string `json:"instance_id,omitempty"` +} + +type AccountSecretsSearchRequest struct { + Keywords string `json:"keywords"` + HostID string `json:"host_id,omitempty"` + Username string `json:"username,omitempty"` +} + +type Checkout struct { + ID string `json:"id"` + Path string `json:"path"` + Type string `json:"type"` + Expires string `json:"expires"` + Created string `json:"created"` + ExplicitCheckout bool `json:"explicit_checkout"` + Secrets []Secrets `json:"secrets"` + Username string `json:"username"` + Email string `json:"email,omitempty"` + FullName string `json:"full_name,omitempty"` + Host HostPrincipals `json:"host,omitempty"` + TargetDomain TargetDomain `json:"target_domain,omitempty"` + ManagedAccountID string `json:"managed_account_id,omitempty"` + UserID string `json:"user_id"` +} + +type CheckoutRequest struct { + Path string `json:"path"` +} + +type Secrets struct { + Version int `json:"version"` + Secret string `json:"secret"` + Created time.Time `json:"created"` +} + +type TargetDomain struct { + ID string `json:"id"` + Name string `json:"name,omitempty"` + Deleted bool `json:"deleted,omitempty"` +} + +type templatesResult struct { + Count int `json:"count"` + Items []CertTemplate `json:"items"` +} + +type accessGroupResult struct { + Count int `json:"count"` + Items []AccessGroup `json:"items"` +} + +type apiCertificateResult struct { + Count int `json:"count"` + Items []APICertificate `json:"items"` +} + +type AccountSecretsResult struct { + Count int `json:"count"` + Items []AccountSecrets `json:"items"` +} + +type CheckoutResult struct { + Count int `json:"count"` + Items []Checkout `json:"items"` +} diff --git a/api/secretsmanager/client.go b/api/secretsmanager/client.go new file mode 100644 index 0000000..a4ab671 --- /dev/null +++ b/api/secretsmanager/client.go @@ -0,0 +1,455 @@ +package secretsmanager + +import ( + "net/url" + + "github.com/SSHcom/privx-sdk-go/common" + "github.com/SSHcom/privx-sdk-go/restapi" +) + +// Client is a secrets-manager client instance. +type Client struct { + api restapi.Connector +} + +// New creates a new secrets-manager client instance +func New(api restapi.Connector) *Client { + return &Client{api: api} +} + +// SecretsManagerStatus get microservice status +func (s *Client) SecretsManagerStatus() (*common.ServiceStatus, error) { + status := &common.ServiceStatus{} + + _, err := s.api. + URL("/secrets-manager/api/v1/status"). + Get(status) + + return status, err +} + +// MARK: Password Policies +// PasswordPolicies lists all password policies +func (s *Client) PasswordPolicies() (PwPolicyResult, error) { + result := PwPolicyResult{} + + _, err := s.api. + URL("/secrets-manager/api/v1/password-policies"). + Get(&result) + + return result, err +} + +// CreatePasswordPolicy create a password policy +func (s *Client) CreatePasswordPolicy(p PasswordPolicy) (string, error) { + var object struct { + ID string `json:"id"` + } + + _, err := s.api. + URL("/secrets-manager/api/v1/password-policy"). + Post(&p, &object) + + return object.ID, err +} + +// PasswordPolicy get password policy by id +func (s *Client) PasswordPolicy(policyId string) (*PasswordPolicy, error) { + p := &PasswordPolicy{} + + _, err := s.api. + URL("/secrets-manager/api/v1/password-policy/%s", url.PathEscape(policyId)). + Get(&p) + + return p, err +} + +// UpdatePasswordPolicy update existing password policy +func (s *Client) UpdatePasswordPolicy(policyId string, p PasswordPolicy) error { + _, err := s.api. + URL("/secrets-manager/api/v1/password-policy/%s", url.PathEscape(policyId)). + Put(p) + + return err +} + +// DeletePasswordPolicy delete a password policy +func (s *Client) DeletePasswordPolicy(policyId string) error { + _, err := s.api. + URL("/secrets-manager/api/v1/password-policy/%s", url.PathEscape(policyId)). + Delete() + + return err +} + +// MARK: Manage passwords +// RotatePassword initiate password rotation +func (s *Client) RotatePassword(hostId, account string) error { + _, err := s.api. + URL("/secrets-manager/api/v1/rotate/%s/%s", url.PathEscape(hostId), url.PathEscape(account)). + Post(nil) + + return err +} + +// MARK: Manage rotation scripts +// ScriptTemplates lists all script templates +func (s *Client) ScriptTemplates() (ScriptTemplateResult, error) { + result := ScriptTemplateResult{} + + _, err := s.api. + URL("/secrets-manager/api/v1/script-templates"). + Get(&result) + + return result, err +} + +// CreateScriptTemplate create a script template +func (s *Client) CreateScriptTemplate(t ScriptTemplate) (string, error) { + var object struct { + ID string `json:"id"` + } + + _, err := s.api. + URL("/secrets-manager/api/v1/script-template"). + Post(&t, &object) + + return object.ID, err +} + +// ScriptTemplate get script template by id +func (s *Client) ScriptTemplate(templateId string) (*ScriptTemplate, error) { + p := &ScriptTemplate{} + + _, err := s.api. + URL("/secrets-manager/api/v1/script-template/%s", url.PathEscape(templateId)). + Get(&p) + + return p, err +} + +// UpdateScriptTemplate update existing script template +func (s *Client) UpdateScriptTemplate(templateId string, t ScriptTemplate) error { + _, err := s.api. + URL("/secrets-manager/api/v1/script-template/%s", url.PathEscape(templateId)). + Put(t) + + return err +} + +// DeleteScriptTemplate delete a script template +func (s *Client) DeleteScriptTemplate(templateId string) error { + _, err := s.api. + URL("/secrets-manager/api/v1/password-policy/%s", url.PathEscape(templateId)). + Delete() + + return err +} + +// CompileScript compile script with test data +func (s *Client) CompileScript(r CompileScriptRequest) (string, error) { + var object struct { + Script string `json:"script"` + } + + _, err := s.api. + URL("/secrets-manager/api/v1/script-template/compile"). + Post(&r, &object) + + return object.Script, err +} + +// MARK: Target domains +// TargetDomains lists all target domains +func (s *Client) TargetDomains(offset, limit int, sortkey, sortdir string) (TdResult, error) { + result := TdResult{} + filters := Params{ + Offset: offset, + Limit: limit, + Sortkey: sortkey, + Sortdir: sortdir, + } + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains"). + Query(&filters). + Get(&result) + + return result, err +} + +// CreateTargetDomain create a target domain +func (s *Client) CreateTargetDomain(td TargetDomain) (string, error) { + var object struct { + ID string `json:"id"` + } + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains"). + Post(&td, &object) + + return object.ID, err +} + +// SearchTargetDomain search for existing target domain +func (s *Client) SearchTargetDomain(sortkey, sortdir string, offset, limit int, searchObject TargetDomainsSearch) (TdResult, error) { + result := TdResult{} + filters := Params{ + Offset: offset, + Limit: limit, + Sortkey: sortkey, + Sortdir: sortdir, + } + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/search"). + Query(&filters). + Post(&searchObject, &result) + + return result, err +} + +// TargetDomain get target domain by id +func (s *Client) TargetDomain(tdId string) (*TargetDomain, error) { + td := &TargetDomain{} + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s", url.PathEscape(tdId)). + Get(&td) + + return td, err +} + +// UpdateTargetDomain update existing target domain +func (s *Client) UpdateTargetDomain(tdId string, td TargetDomain) error { + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s", url.PathEscape(tdId)). + Put(td) + + return err +} + +// DeleteTargetDomain delete a target domain +func (s *Client) DeleteTargetDomain(tdId string) error { + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s", url.PathEscape(tdId)). + Delete() + + return err +} + +// RefreshTargetDomain trigger target domain account scan +func (s *Client) RefreshTargetDomain(tdId string) error { + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/refresh", url.PathEscape(tdId)). + Post(nil) + + return err +} + +// MARK: Target domain accounts +// TargetDomainAccounts lists all accounts in target domain +func (s *Client) TargetDomainAccounts(offset, limit int, sortkey, sortdir, tdId string) (ScannedAccountResult, error) { + result := ScannedAccountResult{} + filters := Params{ + Offset: offset, + Limit: limit, + Sortkey: sortkey, + Sortdir: sortdir, + } + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/accounts", url.PathEscape(tdId)). + Query(&filters). + Get(&result) + + return result, err +} + +// SearchTargetDomainAccounts search accounts in target domain +func (s *Client) SearchTargetDomainAccounts(sortkey, sortdir, tdId string, offset, limit int, searchObject ScannedAccountsSearch) (ScannedAccountResult, error) { + result := ScannedAccountResult{} + filters := Params{ + Offset: offset, + Limit: limit, + Sortkey: sortkey, + Sortdir: sortdir, + } + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/accounts/search", url.PathEscape(tdId)). + Query(&filters). + Post(&searchObject, &result) + + return result, err +} + +// TargetDomainAccount get target domain account +func (s *Client) TargetDomainAccount(tdId, accountId string) (ScannedAccount, error) { + account := ScannedAccount{} + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/accounts/%s", url.PathEscape(tdId), url.PathEscape(accountId)). + Get(&account) + + return account, err +} + +// UpdateTargetDomainAccount update target domain account +func (s *Client) UpdateTargetDomainAccount(tdId, accountId string, change ScannedAccountChangeSet) error { + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/accounts/%s", url.PathEscape(tdId), url.PathEscape(accountId)). + Put(change) + + return err +} + +// BatchUpdateTargetDomain update target domain in batch +func (s *Client) BatchUpdateTargetDomain(tdId string, change ScannedAccountEditBatch) error { + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/accounts/batch/edit", url.PathEscape(tdId)). + Post(change) + + return err +} + +// MARK: Managed accounts +// ManagedAccounts lists all managed accounts in a target domain +func (s *Client) ManagedAccounts(offset, limit int, sortkey, sortdir, tdId string) (ManagedAccountResult, error) { + result := ManagedAccountResult{} + filters := Params{ + Offset: offset, + Limit: limit, + Sortkey: sortkey, + Sortdir: sortdir, + } + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts", url.PathEscape(tdId)). + Query(&filters). + Get(&result) + + return result, err +} + +// CreateManagedAccount create a managed account +func (s *Client) CreateManagedAccount(tdId string, ma ManagedAccount) (string, error) { + var object struct { + ID string `json:"id"` + } + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts", url.PathEscape(tdId)). + Post(&ma, &object) + + return object.ID, err +} + +// SearchManagedAccounts search managed accounts in a target domain +func (s *Client) SearchManagedAccounts(sortkey, sortdir, tdId string, offset, limit int, searchObject ManagedAccountsSearch) (ManagedAccountResult, error) { + result := ManagedAccountResult{} + filters := Params{ + Offset: offset, + Limit: limit, + Sortkey: sortkey, + Sortdir: sortdir, + } + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts/search", url.PathEscape(tdId)). + Query(&filters). + Post(&searchObject, &result) + + return result, err +} + +// ManagedAccount get managed account +func (s *Client) ManagedAccount(tdId, maId string) (ManagedAccount, error) { + account := ManagedAccount{} + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts/%s", url.PathEscape(tdId), url.PathEscape(maId)). + Get(&account) + + return account, err +} + +// UpdateTargetManagedAccount update managed account +func (s *Client) UpdateTargetManagedAccount(tdId, maId string, change ManagedAccount) error { + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts/%s", url.PathEscape(tdId), url.PathEscape(maId)). + Put(change) + + return err +} + +// DeleteManagedAccount delete managed account +func (s *Client) DeleteManagedAccount(tdId, maId string) error { + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts/%s", url.PathEscape(tdId), url.PathEscape(maId)). + Delete() + + return err +} + +// RotateManagedAccountPassword trigger managed account password rotation +func (s *Client) RotateManagedAccountPassword(tdId, maId string) error { + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts/%s/rotate", url.PathEscape(tdId), url.PathEscape(maId)). + Post(nil) + + return err +} + +// ManagedAccountPassword provide password for managed account +func (s *Client) ManagedAccountPassword(tdId, maId, password string) error { + pwReq := ManagedAccountPasswordRequest{ + Password: password, + } + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts/%s/password", url.PathEscape(tdId), url.PathEscape(maId)). + Post(pwReq) + + return err +} + +// BatchCreateManagedAccount create a batch of managed accounts +func (s *Client) BatchCreateManagedAccount(tdId string, ma ManagedAccountCreateBatch) ([]string, error) { + var object struct { + IDs []string `json:"ids"` + } + + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts/batch/create", url.PathEscape(tdId)). + Post(&ma, &object) + + return object.IDs, err +} + +// BatchUpdateManagedAccount update a batch of managed accounts +func (s *Client) BatchUpdateManagedAccount(tdId string, change ManagedAccountChangeSet) error { + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts/batch/edit", url.PathEscape(tdId)). + Post(&change) + + return err +} + +// BatchDeleteManagedAccount delete a batch of managed accounts +func (s *Client) BatchDeleteManagedAccount(tdId string, delete ManagedAccountBatch) error { + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts/batch/delete", url.PathEscape(tdId)). + Post(&delete) + + return err +} + +// BatchRotateManagedAccount rotate a batch of managed accounts +func (s *Client) BatchRotateManagedAccount(tdId string, rotate ManagedAccountBatch) error { + _, err := s.api. + URL("/secrets-manager/api/v1/targetdomains/%s/managedaccounts/batch/rotate", url.PathEscape(tdId)). + Post(&rotate) + + return err +} diff --git a/api/secretsmanager/model.go b/api/secretsmanager/model.go new file mode 100644 index 0000000..fe2d505 --- /dev/null +++ b/api/secretsmanager/model.go @@ -0,0 +1,277 @@ +package secretsmanager + +import "time" + +type Params struct { + Offset int `json:"offset,omitempty"` + Limit int `json:"limit,omitempty"` + Sortdir string `json:"sortdir,omitempty"` + Sortkey string `json:"sortkey,omitempty"` +} + +type PasswordPolicy struct { + ID string `json:"id"` + Name string `json:"name"` + RotationInterval string `json:"rotation_interval"` + PasswordMinLength int `json:"password_min_length"` + PasswordMaxLength int `json:"password_max_length" ` + UseSpecialCharacters bool `json:"use_special_characters"` + UseLowercase bool `json:"use_lower_case"` + UseUppercase bool `json:"use_upper_case"` + UseNumbers bool `json:"use_numbers"` + MaxVersions int `json:"max_versions"` + NumberOfRetries int `json:"number_of_retries"` + RetryInterval string `json:"retry_interval"` + MaxConcurrentCheckouts int `json:"max_concurrent_checkouts"` + MaxCheckoutDuration string `json:"max_checkout_duration"` + RotateOnRelease bool `json:"rotate_on_release"` + VerifyAfterRotation bool `json:"verify_after_rotation"` + Created *time.Time `json:"created"` + CreatedBy string `json:"created_by"` + Updated *time.Time `json:"updated"` + UpdatedBy string `json:"updated_by"` +} + +type ScriptTemplate struct { + ID string `json:"id"` + Name string `json:"name"` + OperatingSystem string `json:"operating_system"` + Script string `json:"script"` + Created *time.Time `json:"created"` + CreatedBy string `json:"created_by"` + Updated *time.Time `json:"updated"` + UpdatedBy string `json:"updated_by"` +} + +type CompileScriptRequest struct { + OperatingSystem string `json:"operating_system"` + Script string `json:"script"` +} + +type TargetDomain struct { + ID string `json:"id"` + Name string `json:"name"` + Enabled bool `json:"enabled"` + PeriodicScan bool `json:"periodic_scan"` + PeriodicScanInterval int `json:"periodic_scan_interval,omitempty"` + ScanStatus string `json:"scan_status,omitempty"` + ScanMessage string `json:"scan_message,omitempty"` + LastScanned *time.Time `json:"last_scanned,omitempty"` + AutoOnboarding bool `json:"auto_onboarding"` + AutoOnboardingPolicy *PasswordPolicyHandle `json:"auto_onboarding_policy,omitempty"` + EndPoints []TargetDomainEndpoint `json:"endpoints"` + Comment string `json:"comment"` + Created time.Time `json:"created,omitempty"` + Author string `json:"author,omitempty"` + Updated time.Time `json:"updated,omitempty"` + UpdatedBy string `json:"updated_by,omitempty"` +} + +type PasswordPolicyHandle struct { + ID string `json:"id,omitempty"` + Name string `json:"name,omitempty"` + Deleted bool `json:"deleted,omitempty"` +} + +type TargetDomainEndpoint struct { + TargetDomainID string `json:"-"` + Type string `json:"type"` + ScanPriority int `json:"scan_priority"` + RotationPriority int `json:"rotation_priority"` + AttributeMapping map[string]string `json:"attribute_mapping,omitempty"` + LdapProtocol string `json:"ldap_protocol,omitempty"` + LdapAddress string `json:"ldap_address,omitempty"` + LdapPort int `json:"ldap_port,omitempty"` + LdapBaseDN string `json:"ldap_base_dn,omitempty"` + LdapBindDN string `json:"ldap_bind_dn,omitempty"` + LdapBindPassword string `json:"ldap_bind_password,omitempty"` + LdapUserFilter string `json:"ldap_user_filter,omitempty"` + LdapRootCertificates string `json:"ldap_root_certificates,omitempty"` + LdapSkipStrictCertCheck bool `json:"ldap_skip_strict_cert_check,omitempty"` + EntraBaseUrl string `json:"entra_base_url,omitempty"` + EntraTenantID string `json:"entra_tenant_id,omitempty"` + EntraClientID string `json:"entra_client_id,omitempty"` + EntraClientSecret string `json:"entra_client_secret,omitempty"` + EntraBatchSize int `json:"entra_batch_size,omitempty"` + EntraPageSize int `json:"entra_page_size,omitempty"` + EntraGroupFilter []string `json:"entra_group_filter,omitempty"` +} + +type TargetDomainsSearch struct { + Keywords string `json:"keywords,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + PeriodicScan *bool `json:"periodic_scan,omitempty"` + AutoOnboarding *bool `json:"auto_onboarding,omitempty"` +} + +type ScannedAccount struct { + ID string `json:"id"` + Username string `json:"username"` + Email string `json:"email"` + FullName string `json:"full_name"` + SourceID string `json:"source_id"` + SecurityID string `json:"security_id"` + AdditionalData map[string]string `json:"additional_data"` + TargetDomain TargetDomainHandle `json:"target_domain"` + State string `json:"state"` + Ignored bool `json:"ignored"` + Comment string `json:"comment"` + Created time.Time `json:"created,omitempty"` + Updated time.Time `json:"updated,omitempty"` + UpdatedBy string `json:"updated_by,omitempty"` +} + +type TargetDomainHandle struct { + ID string `json:"id"` + Name string `json:"name,omitempty"` + Deleted bool `json:"deleted,omitempty"` +} + +type ScannedAccountsSearch struct { + Keywords string `json:"keywords,omitempty"` + CreatedAfter *time.Time `json:"created_after,omitempty"` + CreatedBefore *time.Time `json:"created_before,omitempty"` + UpdatedAfter *time.Time `json:"updated_after,omitempty"` + UpdatedBefore *time.Time `json:"updated_before,omitempty"` + State string `json:"state,omitempty"` + Ignored *bool `json:"ignored,omitempty"` +} + +type ScannedAccountChangeSet struct { + Ignored *bool `json:"ignored,omitempty"` + Comment *string `json:"comment,omitempty"` +} + +type ScannedAccountEditBatch struct { + IDs []string `json:"ids"` + ChangeSet ScannedAccountChangeSet `json:"changes"` +} + +type ManagedAccount struct { + ID string `json:"id"` + Username string `json:"username"` + Email string `json:"email,omitempty"` + FullName string `json:"full_name,omitempty"` + SourceID string `json:"source_id,omitempty"` + SecurityID string `json:"security_id,omitempty"` + AdditionalData map[string]string `json:"additional_data,omitempty"` + TargetDomain TargetDomainHandle `json:"target_domain"` + PasswordPolicy *PasswordPolicyHandle `json:"password_policy,omitempty"` + Enabled bool `json:"enabled"` + RotationEnabled bool `json:"rotation_enabled"` + ExplicitCheckout bool `json:"explicit_checkout"` + State string `json:"state"` + Comment string `json:"comment,omitempty"` + SecretName string `json:"secret_name,omitempty"` + Locked bool `json:"locked"` + LockedTimestamp *time.Time `json:"locked_timestamp,omitempty"` + RotationHistory []SecretRotationEvent `json:"rotation_history,omitempty"` + SecretCheckouts []SecretCheckout `json:"checkouts,omitempty"` + Created time.Time `json:"created,omitempty"` + Author string `json:"author,omitempty"` + Updated *time.Time `json:"updated,omitempty"` + UpdatedBy string `json:"updated_by,omitempty"` +} + +type SecretRotationEvent struct { + Version int `json:"version"` + Rotated time.Time `json:"rotated"` + Trigger string `json:"trigger"` + Status string `json:"status"` +} + +type SecretCheckout struct { + ID string `json:"id"` + Type string `json:"type"` + UserID string `json:"user_id"` + Expires time.Time `json:"expires"` + Created time.Time `json:"created"` + ExplicitCheckout bool `json:"explicit_checkout"` + Secrets []SecretVersion `json:"secrets,omitempty"` + Username string `json:"username"` + Email string `json:"email,omitempty"` + FullName string `json:"full_name,omitempty"` + TargetDomainID string `json:"target_domain_id,omitempty"` + ManagedAccountID string `json:"managed_account_id,omitempty"` + HostID string `json:"host_id,omitempty"` + SecretName string `json:"secret_name,omitempty"` + Meta string `json:"meta,omitempty"` +} + +type SecretVersion struct { + Version int `json:"version"` + Secret string `json:"secret"` + Created time.Time `json:"created"` +} + +type ManagedAccountsSearch struct { + Keywords string `json:"keywords,omitempty"` + Enabled *bool `json:"enabled,omitempty"` + CreatedAfter *time.Time `json:"created_after,omitempty"` + CreatedBefore *time.Time `json:"created_before,omitempty"` + UpdatedAfter *time.Time `json:"updated_after,omitempty"` + UpdatedBefore *time.Time `json:"updated_before,omitempty"` + State string `json:"state,omitempty"` + RotationEnabled *bool `json:"rotation_enabled,omitempty"` + ExplicitCheckout *bool `json:"explicit_checkout,omitempty"` +} + +type ManagedAccountPasswordRequest struct { + Password string `json:"password"` +} + +type ManagedAccountCreateBatch struct { + IDs []string `json:"ids"` + Data ManagedAccountCreateData `json:"data"` +} + +type ManagedAccountCreateData struct { + Enabled bool `json:"enabled"` + RotationEnabled bool `json:"rotation_enabled"` + Rotate bool `json:"rotate"` + ExplicitCheckout bool `json:"explicit_checkout"` + PasswordPolicy PasswordPolicyHandle `json:"password_policy,omitempty"` + Comment string `json:"comment,omitempty"` +} + +type ManagedAccountEditBatch struct { + IDs []string `json:"ids"` + ChangeSet ManagedAccountChangeSet `json:"changes"` +} + +type ManagedAccountChangeSet struct { + Enabled *bool `json:"enabled"` + RotationEnabled *bool `json:"rotation_enabled"` + ExplicitCheckout *bool `json:"explicit_checkout"` + PasswordPolicy *PasswordPolicyHandle `json:"password_policy,omitempty"` + Comment *string `json:"comment,omitempty"` +} + +type ManagedAccountBatch struct { + IDs []string `json:"ids"` +} + +type PwPolicyResult struct { + Count int `json:"count"` + Items []PasswordPolicy `json:"items"` +} + +type ScriptTemplateResult struct { + Count int `json:"count"` + Items []ScriptTemplate `json:"items"` +} + +type TdResult struct { + Count int `json:"count"` + Items []TargetDomain `json:"items"` +} + +type ScannedAccountResult struct { + Count int `json:"count"` + Items []ScannedAccount `json:"items"` +} + +type ManagedAccountResult struct { + Count int `json:"count"` + Items []ManagedAccount `json:"items"` +} diff --git a/go.mod b/go.mod index 74805e7..4dc0c11 100644 --- a/go.mod +++ b/go.mod @@ -2,14 +2,4 @@ module github.com/SSHcom/privx-sdk-go go 1.21 -require ( - github.com/BurntSushi/toml v1.3.2 - github.com/dustin/go-humanize v1.0.1 - github.com/stretchr/testify v1.8.4 -) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) +require github.com/BurntSushi/toml v1.3.2 diff --git a/go.sum b/go.sum index 5e8094d..ef0f966 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,2 @@ github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkce/pkce_test.go b/pkce/pkce_test.go index b08f058..87e5583 100644 --- a/pkce/pkce_test.go +++ b/pkce/pkce_test.go @@ -9,8 +9,6 @@ package pkce import ( "reflect" "testing" - - "github.com/stretchr/testify/assert" ) func TestPKCE(t *testing.T) { @@ -20,7 +18,9 @@ func TestPKCE(t *testing.T) { } challenge, method := verifier.ChallengeS256() - assert.True(t, verifier.Verify(challenge, method), "Should return true") + if !verifier.Verify(challenge, method) { + t.Error("Expected verifier.Verify to return true") + } } func TestPKCEVerify(t *testing.T) { @@ -30,7 +30,9 @@ func TestPKCEVerify(t *testing.T) { } challenge, _ := verifier.ChallengeS256() - assert.False(t, verifier.Verify(challenge, "S512"), "Should return false") + if verifier.Verify(challenge, "S512") { + t.Error("Expected verifier.Verify to return false") + } } func TestString(t *testing.T) { @@ -41,5 +43,7 @@ func TestString(t *testing.T) { result := reflect.TypeOf(verifier.String()).Kind() - assert.EqualValues(t, reflect.String, result, "Expected type string") + if result != reflect.String { + t.Errorf("Expected type string, but got %v", result) + } } diff --git a/restapi/client.go b/restapi/client.go index 71e9cc1..6f4ca7d 100644 --- a/restapi/client.go +++ b/restapi/client.go @@ -12,6 +12,7 @@ import ( "fmt" "io" "log" + "math" "net" "net/http" "net/url" @@ -19,8 +20,6 @@ import ( "strconv" "strings" "time" - - "github.com/dustin/go-humanize" ) // tClient is an HTTP client instance. @@ -219,7 +218,28 @@ func (wc *WriteCounter) Write(p []byte) (int, error) { func (wc *WriteCounter) printProgress() { fmt.Printf("\r%s", strings.Repeat(" ", 50)) - fmt.Printf("\rDownloading... %s complete", humanize.Bytes(wc.Total)) + fmt.Printf("\rDownloading... %s complete", fBytes(wc.Total)) +} + +// Func insp: https://github.com/dustin/go-humanize +func fBytes(s uint64) string { + sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"} + return humanateBytes(s, 1000, sizes) +} + +func humanateBytes(s uint64, base float64, sizes []string) string { + if s < 10 { + return fmt.Sprintf("%d B", s) + } + e := math.Floor(math.Log(float64(s)) / math.Log(base)) + suffix := sizes[int(e)] + val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10 + f := "%.0f %s" + if val < 10 { + f = "%.1f %s" + } + + return fmt.Sprintf(f, val, suffix) } func writeToFile(filename string, resp *http.Response) error { diff --git a/restapi/errors_test.go b/restapi/errors_test.go index 3902ce1..73a5659 100644 --- a/restapi/errors_test.go +++ b/restapi/errors_test.go @@ -13,8 +13,6 @@ import ( "net/http" "net/http/httptest" "testing" - - "github.com/stretchr/testify/assert" ) func TestEmptyResponseBody(t *testing.T) { @@ -23,15 +21,20 @@ func TestEmptyResponseBody(t *testing.T) { resp, _ := mockResponse() result := ErrorFromResponse(resp, emptyRespBody) - assert.EqualValues(t, fmt.Errorf("HTTP error: %s", resp.Status), result, "Expected to be equal error status") + expectedError := fmt.Errorf("HTTP error: %s", resp.Status) + if result.Error() != expectedError.Error() { + t.Errorf("Expected error %v, but got %v", expectedError, result) + } } func TestUnexpectedResponseBody(t *testing.T) { resp, body := mockResponse() result := ErrorFromResponse(resp, body) - assert.EqualValues(t, fmt.Errorf("HTTP error: 200 OK (unexpected response body: invalid character '<' looking for beginning of value)"), - result, "Expected to be equal error status") + expectedError := fmt.Errorf("HTTP error: 200 OK (unexpected response body: invalid character '<' looking for beginning of value)") + if result.Error() != expectedError.Error() { + t.Errorf("Expected error %v, but got %v", expectedError, result) + } } func TestDetailsErrorMessage(t *testing.T) { @@ -39,8 +42,8 @@ func TestDetailsErrorMessage(t *testing.T) { ErrorCode: "42", ErrorMessage: "DtlTest", Property: "Detail", - }, - } + }} + body, _ := json.Marshal(ErrorResponse{ ErrorCode: "42", ErrorMessage: "ErrRspTest", @@ -51,8 +54,10 @@ func TestDetailsErrorMessage(t *testing.T) { resp, _ := mockResponse() result := ErrorFromResponse(resp, body) - assert.EqualValues(t, fmt.Errorf("error: 42, message: ErrRspTest, property: ErrRsp, {error: 42, message: DtlTest, property: Detail}"), - result, "Expected to be equal error status") + expectedError := fmt.Errorf("error: 42, message: ErrRspTest, property: ErrRsp, {error: 42, message: DtlTest, property: Detail}") + if result.Error() != expectedError.Error() { + t.Errorf("Expected error %v, but got %v", expectedError, result) + } } func mockResponse() (*http.Response, []byte) {