-
Notifications
You must be signed in to change notification settings - Fork 258
feat: Add postgresql_security_label
resource
#482
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
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
7f2ab3b
Add SECURITY LABEL provider to acceptance test postgres db.
jbunting 1deb31b
fix quote ident for objectname and provider
jbunting 4f1956f
Apply suggestions from code review
stanleyz 59f141d
Update to reflect what's reviewed
stanleyz bc6c172
Pass in PGVersion in test
stanleyz 2258c1c
Add ARG to Dockerfile
stanleyz 840a8fe
Merge branch 'main' into feat/security-label
cyrilgdn b879182
Make sure the dev library is same as server version
stanleyz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 |
---|---|---|
@@ -0,0 +1,198 @@ | ||
package postgresql | ||
|
||
import ( | ||
"bytes" | ||
"database/sql" | ||
"fmt" | ||
"log" | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" | ||
"github.com/lib/pq" | ||
) | ||
|
||
const ( | ||
securityLabelObjectNameAttr = "object_name" | ||
securityLabelObjectTypeAttr = "object_type" | ||
securityLabelProviderAttr = "label_provider" | ||
securityLabelLabelAttr = "label" | ||
) | ||
|
||
func resourcePostgreSQLSecurityLabel() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: PGResourceFunc(resourcePostgreSQLSecurityLabelCreate), | ||
Read: PGResourceFunc(resourcePostgreSQLSecurityLabelRead), | ||
Update: PGResourceFunc(resourcePostgreSQLSecurityLabelUpdate), | ||
Delete: PGResourceFunc(resourcePostgreSQLSecurityLabelDelete), | ||
Importer: &schema.ResourceImporter{ | ||
StateContext: schema.ImportStatePassthroughContext, | ||
}, | ||
|
||
Schema: map[string]*schema.Schema{ | ||
securityLabelObjectNameAttr: { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "The name of the existing object to apply the security label to", | ||
}, | ||
securityLabelObjectTypeAttr: { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "The type of the existing object to apply the security label to", | ||
}, | ||
securityLabelProviderAttr: { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: true, | ||
Description: "The provider to apply the security label for", | ||
}, | ||
securityLabelLabelAttr: { | ||
Type: schema.TypeString, | ||
Required: true, | ||
ForceNew: false, | ||
Description: "The label to be applied", | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourcePostgreSQLSecurityLabelCreate(db *DBConnection, d *schema.ResourceData) error { | ||
if !db.featureSupported(featureSecurityLabel) { | ||
return fmt.Errorf( | ||
"security Label is not supported for this Postgres version (%s)", | ||
db.version, | ||
) | ||
} | ||
log.Printf("[DEBUG] PostgreSQL security label Create") | ||
label := d.Get(securityLabelLabelAttr).(string) | ||
if err := resourcePostgreSQLSecurityLabelUpdateImpl(db, d, pq.QuoteLiteral(label)); err != nil { | ||
return err | ||
} | ||
|
||
d.SetId(generateSecurityLabelID(d)) | ||
|
||
return resourcePostgreSQLSecurityLabelReadImpl(db, d) | ||
} | ||
|
||
func resourcePostgreSQLSecurityLabelUpdateImpl(db *DBConnection, d *schema.ResourceData, label string) error { | ||
b := bytes.NewBufferString("SECURITY LABEL ") | ||
|
||
objectType := d.Get(securityLabelObjectTypeAttr).(string) | ||
objectName := d.Get(securityLabelObjectNameAttr).(string) | ||
provider := d.Get(securityLabelProviderAttr).(string) | ||
fmt.Fprint(b, " FOR ", pq.QuoteIdentifier(provider)) | ||
fmt.Fprint(b, " ON ", objectType, pq.QuoteIdentifier(objectName)) | ||
fmt.Fprint(b, " IS ", label) | ||
|
||
if _, err := db.Exec(b.String()); err != nil { | ||
log.Printf("[WARN] PostgreSQL security label Create failed %s", err) | ||
return fmt.Errorf("could not create security label: %w", err) | ||
} | ||
return nil | ||
} | ||
|
||
func resourcePostgreSQLSecurityLabelRead(db *DBConnection, d *schema.ResourceData) error { | ||
if !db.featureSupported(featureSecurityLabel) { | ||
return fmt.Errorf( | ||
"Security Label is not supported for this Postgres version (%s)", | ||
db.version, | ||
) | ||
} | ||
log.Printf("[DEBUG] PostgreSQL security label Read") | ||
|
||
return resourcePostgreSQLSecurityLabelReadImpl(db, d) | ||
} | ||
|
||
func resourcePostgreSQLSecurityLabelReadImpl(db *DBConnection, d *schema.ResourceData) error { | ||
objectType := d.Get(securityLabelObjectTypeAttr).(string) | ||
objectName := d.Get(securityLabelObjectNameAttr).(string) | ||
provider := d.Get(securityLabelProviderAttr).(string) | ||
|
||
txn, err := startTransaction(db.client, "") | ||
if err != nil { | ||
return err | ||
} | ||
defer deferredRollback(txn) | ||
|
||
query := "SELECT objtype, provider, objname, label FROM pg_seclabels WHERE objtype = $1 and objname = $2 and provider = $3" | ||
row := db.QueryRow(query, objectType, quoteIdentifier(objectName), quoteIdentifier(provider)) | ||
|
||
var label, newObjectName, newProvider string | ||
err = row.Scan(&objectType, &newProvider, &newObjectName, &label) | ||
switch { | ||
case err == sql.ErrNoRows: | ||
log.Printf("[WARN] PostgreSQL security label for (%s '%s') with provider %s not found", objectType, objectName, provider) | ||
d.SetId("") | ||
return nil | ||
case err != nil: | ||
return fmt.Errorf("Error reading security label: %w", err) | ||
} | ||
|
||
if quoteIdentifier(objectName) != newObjectName || quoteIdentifier(provider) != newProvider { | ||
// In reality, this should never happen, but if it does, we want to make sure that the state is in sync with the remote system | ||
// This will trigger a TF error saying that the provider has a bug if it ever happens | ||
objectName = newObjectName | ||
provider = newProvider | ||
} | ||
d.Set(securityLabelObjectTypeAttr, objectType) | ||
d.Set(securityLabelObjectNameAttr, objectName) | ||
d.Set(securityLabelProviderAttr, provider) | ||
d.Set(securityLabelLabelAttr, label) | ||
d.SetId(generateSecurityLabelID(d)) | ||
|
||
return nil | ||
} | ||
|
||
func resourcePostgreSQLSecurityLabelDelete(db *DBConnection, d *schema.ResourceData) error { | ||
if !db.featureSupported(featureSecurityLabel) { | ||
return fmt.Errorf( | ||
"Security Label is not supported for this Postgres version (%s)", | ||
db.version, | ||
) | ||
} | ||
log.Printf("[DEBUG] PostgreSQL security label Delete") | ||
|
||
if err := resourcePostgreSQLSecurityLabelUpdateImpl(db, d, "NULL"); err != nil { | ||
return err | ||
} | ||
|
||
d.SetId("") | ||
|
||
return nil | ||
} | ||
|
||
func resourcePostgreSQLSecurityLabelUpdate(db *DBConnection, d *schema.ResourceData) error { | ||
if !db.featureSupported(featureServer) { | ||
return fmt.Errorf( | ||
"Security Label is not supported for this Postgres version (%s)", | ||
db.version, | ||
) | ||
} | ||
log.Printf("[DEBUG] PostgreSQL security label Update") | ||
|
||
label := d.Get(securityLabelLabelAttr).(string) | ||
if err := resourcePostgreSQLSecurityLabelUpdateImpl(db, d, pq.QuoteLiteral(label)); err != nil { | ||
return err | ||
} | ||
|
||
return resourcePostgreSQLSecurityLabelReadImpl(db, d) | ||
} | ||
|
||
func generateSecurityLabelID(d *schema.ResourceData) string { | ||
return strings.Join([]string{ | ||
d.Get(securityLabelProviderAttr).(string), | ||
d.Get(securityLabelObjectTypeAttr).(string), | ||
d.Get(securityLabelObjectNameAttr).(string), | ||
}, ".") | ||
} | ||
|
||
func quoteIdentifier(s string) string { | ||
var result = s | ||
re := regexp.MustCompile(`^[a-zA-Z_][a-zA-Z0-9_]*$`) | ||
cyrilgdn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if !re.MatchString(s) || s != strings.ToLower(s) { | ||
result = pq.QuoteIdentifier(s) | ||
} | ||
return result | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.