Skip to content

Commit

Permalink
fix: codefresh_context disable decrypt when forbidDecrypt feature fla…
Browse files Browse the repository at this point in the history
…g is set (#156)
  • Loading branch information
ilia-medvedev-codefresh authored Dec 17, 2024
1 parent f41f07f commit 59f99a0
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 31 deletions.
41 changes: 31 additions & 10 deletions codefresh/cfclient/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ import (

// Client token, host, htpp.Client
type Client struct {
Token string
TokenHeader string
Host string
HostV2 string
Client *http.Client
Token string
TokenHeader string
Host string
HostV2 string
featureFlags map[string]bool
Client *http.Client
}

// RequestOptions path, method, etc
Expand All @@ -35,11 +36,12 @@ func NewClient(hostname string, hostnameV2 string, token string, tokenHeader str
tokenHeader = "Authorization"
}
return &Client{
Host: hostname,
HostV2: hostnameV2,
Token: token,
TokenHeader: tokenHeader,
Client: &http.Client{},
Host: hostname,
HostV2: hostnameV2,
Token: token,
TokenHeader: tokenHeader,
Client: &http.Client{},
featureFlags: map[string]bool{},
}

}
Expand Down Expand Up @@ -112,6 +114,25 @@ func (client *Client) RequestApiXAccessToken(opt *RequestOptions) ([]byte, error
return body, nil
}

func (client *Client) isFeatureFlagEnabled(flagName string) (bool, error) {

if len(client.featureFlags) == 0 {
currAcc, err := client.GetCurrentAccount()

if err != nil {
return false, err
}

client.featureFlags = currAcc.FeatureFlags
}

if val, ok := client.featureFlags[flagName]; ok {
return val, nil
}

return false, nil
}

// ToQS add extra parameters to path
func ToQS(qs map[string]string) string {
var arr = []string{}
Expand Down
40 changes: 35 additions & 5 deletions codefresh/cfclient/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,17 @@ import (
"fmt"
"log"
"net/url"

"golang.org/x/exp/slices"
)

var encryptedContextTypes = []string{
"secret",
"secret-yaml",
"storage.s3",
"storage.azuref",
}

type ContextErrorResponse struct {
Status int `json:"status,omitempty"`
Message string `json:"message,omitempty"`
Expand All @@ -17,9 +26,10 @@ type ContextMetadata struct {
}

type Context struct {
Metadata ContextMetadata `json:"metadata,omitempty"`
Spec ContextSpec `json:"spec,omitempty"`
Version string `json:"version,omitempty"`
Metadata ContextMetadata `json:"metadata,omitempty"`
Spec ContextSpec `json:"spec,omitempty"`
Version string `json:"version,omitempty"`
IsEncrypred bool `json:"isEncrypted,omitempty"`
}

type ContextSpec struct {
Expand All @@ -32,7 +42,18 @@ func (context *Context) GetID() string {
}

func (client *Client) GetContext(name string) (*Context, error) {
fullPath := fmt.Sprintf("/contexts/%s?decrypt=true", url.PathEscape(name))
fullPath := fmt.Sprintf("/contexts/%s", url.PathEscape(name))

forbidDecrypt, err := client.isFeatureFlagEnabled("forbidDecrypt")

if err != nil {
forbidDecrypt = false
}

if !forbidDecrypt {
fullPath += "?decrypt=true"
}

opts := RequestOptions{
Path: fullPath,
Method: "GET",
Expand All @@ -49,8 +70,17 @@ func (client *Client) GetContext(name string) (*Context, error) {
return nil, err
}

return &respContext, nil
// This is so not to break existing behavior while adding support for forbidDecrypt feature flag
// The provider used to always decrypt the contexts, hence we treat all contexts as decrypted unless forbidDecrypt is set
isEncryptedType := slices.Contains(encryptedContextTypes, respContext.Spec.Type)

respContext.IsEncrypred = false

if forbidDecrypt && isEncryptedType {
respContext.IsEncrypred = true
}

return &respContext, nil
}

func (client *Client) CreateContext(context *Context) (*Context, error) {
Expand Down
21 changes: 14 additions & 7 deletions codefresh/cfclient/current_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,11 @@ type CurrentAccountUser struct {

// CurrentAccount spec
type CurrentAccount struct {
ID string
Name string
Users []CurrentAccountUser
Admins []CurrentAccountUser
ID string
Name string
Users []CurrentAccountUser
Admins []CurrentAccountUser
FeatureFlags map[string]bool
}

// GetCurrentAccount -
Expand All @@ -46,9 +47,10 @@ func (client *Client) GetCurrentAccount() (*CurrentAccount, error) {
return nil, fmt.Errorf("GetCurrentAccount - cannot get activeAccountName")
}
currentAccount := &CurrentAccount{
Name: activeAccountName,
Users: make([]CurrentAccountUser, 0),
Admins: make([]CurrentAccountUser, 0),
Name: activeAccountName,
Users: make([]CurrentAccountUser, 0),
Admins: make([]CurrentAccountUser, 0),
FeatureFlags: make(map[string]bool),
}

accountAdminsIDs := make([]string, 0)
Expand All @@ -62,6 +64,11 @@ func (client *Client) GetCurrentAccount() (*CurrentAccount, error) {
for _, adminI := range admins {
accountAdminsIDs = append(accountAdminsIDs, adminI.(string))
}
featureFlags := accX.Get("features").ObjxMap()

for k, v := range featureFlags {
currentAccount.FeatureFlags[k] = v.(bool)
}
break
}
}
Expand Down
2 changes: 1 addition & 1 deletion codefresh/context/storage.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ func flattenStorageContextConfig(spec cfclient.ContextSpec, auth map[string]inte
func FlattenJsonConfigStorageContextConfig(spec cfclient.ContextSpec) []interface{} {
auth := make(map[string]interface{})
auth["json_config"] = spec.Data["auth"].(map[string]interface{})["jsonConfig"]
auth["type"] = spec.Data["type"]
auth["type"] = spec.Data["auth"].(map[string]interface{})["type"]
return flattenStorageContextConfig(spec, auth)
}

Expand Down
2 changes: 1 addition & 1 deletion codefresh/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (

"github.com/codefresh-io/terraform-provider-codefresh/codefresh/cfclient"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

"os"
)

Expand Down Expand Up @@ -87,5 +86,6 @@ func configureProvider(d *schema.ResourceData) (interface{}, error) {
if token == "" {
token = os.Getenv(ENV_CODEFRESH_API_KEY)
}

return cfclient.NewClient(apiURL, apiURLV2, token, ""), nil
}
17 changes: 12 additions & 5 deletions codefresh/resource_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,14 @@ func resourceContextRead(d *schema.ResourceData, meta interface{}) error {
}

context, err := client.GetContext(contextName)

if err != nil {
log.Printf("[DEBUG] Error while getting context. Error = %v", contextName)
return err
}

err = mapContextToResource(*context, d)

if err != nil {
log.Printf("[DEBUG] Error while mapping context to resource. Error = %v", err)
return err
Expand Down Expand Up @@ -225,14 +227,20 @@ func resourceContextDelete(d *schema.ResourceData, meta interface{}) error {
func mapContextToResource(context cfclient.Context, d *schema.ResourceData) error {

err := d.Set("name", context.Metadata.Name)

if err != nil {
return err
}

err = d.Set("spec", flattenContextSpec(context.Spec))
if err != nil {
log.Printf("[DEBUG] Failed to flatten Context spec = %v", context.Spec)
return err
// Read spec from API if context is not encrypted or forbitDecrypt is not set
if !context.IsEncrypred {

err = d.Set("spec", flattenContextSpec(context.Spec))

if err != nil {
log.Printf("[DEBUG] Failed to flatten Context spec = %v", context.Spec)
return err
}
}

return nil
Expand All @@ -253,7 +261,6 @@ func flattenContextSpec(spec cfclient.ContextSpec) []interface{} {
case contextAzureStorage:
m[schemautil.MustNormalizeFieldName(currentContextType)] = storageContext.FlattenAzureStorageContextConfig(spec)
default:
log.Printf("[DEBUG] Invalid context type = %v", currentContextType)
return nil
}

Expand Down
4 changes: 2 additions & 2 deletions codefresh/resource_context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ resource "codefresh_context" "test" {
spec {
config {
data = {
data = {
%q = %q
%q = %q
}
Expand All @@ -223,7 +223,7 @@ resource "codefresh_context" "test" {
spec {
secret {
data = {
data = {
%q = %q
%q = %q
}
Expand Down
77 changes: 77 additions & 0 deletions docs/resources/context.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ YAML
```hcl
resource "codefresh_context" "test-secret-yaml" {
name = "my-shared-secret-yaml"
decrypt_spec = false
spec {
# NOTE: The `-` from secret-yaml is stripped because the character is not allowed in Field name
# File passed MUST be a valid YAML
Expand All @@ -96,6 +97,82 @@ resource "codefresh_context" "test-secret-yaml" {
}
```

#### AWS S3 storage context

```hcl
resource "codefresh_context" "test-s3" {
name = "my-s3-context"
decrypt_spec = false
spec {
storages3 {
data {
auth {
type = "basic"
json_config = {accessKeyId = "key", secretAccessKey = "secret"}
}
}
}
}
}
```

#### Azure file storage context

```hcl
resource "codefresh_context" "test-azure" {
name = "my-azure-file-context"
decrypt_spec = false
spec {
storageazuref {
data {
auth {
type = "basic"
account_name = "account"
account_key = "key"
}
}
}
}
}
```

#### Google cloud storage context

```hcl
resource "codefresh_context" "test-google-cloud-storage" {
name = "my-gcs-context"
spec {
storagegc {
data {
auth {
type = "basic"
json_config = jsondecode(<<EOF
{
"type": "service_account",
"project_id": "PROJECT_ID",
"private_key_id": "KEY_ID",
"private_key": "-----BEGIN PRIVATE KEY-----\nPRIVATE_KEY\n-----END PRIVATE KEY-----\n",
"client_email": "SERVICE_ACCOUNT_EMAIL",
"client_id": "CLIENT_ID",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/SERVICE_ACCOUNT_EMAIL"
}
EOF
)
}
}
}
}
}
```

<!-- schema generated by tfplugindocs -->
## Schema

Expand Down
Loading

0 comments on commit 59f99a0

Please sign in to comment.