Skip to content

Commit

Permalink
postgres: Expand grant privileges where required
Browse files Browse the repository at this point in the history
Inside postgresql, some privileges are altered[0] when issuing a `GRANT`
- for example `ALL` is expanded to `CREATE, TEMPORARY, CONNECT`.

When we observe the grant, we query privileges on the database to see if
the ones in the grant exist. Given that they are expanded, we can't
simply query for what the user gave us. Expand the privileges which need
expanding before we make the query.

Closes: #92

[0]: https://www.postgresql.org/docs/15/ddl-priv.html

Signed-off-by: Iain Lane <[email protected]>
  • Loading branch information
iainlane committed Jul 27, 2023
1 parent c6c358b commit 15aa531
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 1 deletion.
33 changes: 33 additions & 0 deletions apis/postgresql/v1alpha1/grant_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,39 @@ type GrantPrivilege string
// +kubebuilder:validation:MinItems:=1
type GrantPrivileges []GrantPrivilege

// Some privileges are shorthands for multiple privileges. These translations
// happen internally inside postgresql when making grants. When we query the
// privileges back, we need to look for the expanded set.
// https://www.postgresql.org/docs/15/ddl-priv.html
var grantReplacements = map[GrantPrivilege]GrantPrivileges{
"ALL": {"CREATE", "TEMPORARY", "CONNECT"},
"ALL PRIVILEGES": {"CREATE", "TEMPORARY", "CONNECT"},
"TEMP": {"TEMPORARY"},
}

// ExpandPrivileges expands any shorthand privileges to their full equivalents.
func (gp *GrantPrivileges) ExpandPrivileges() GrantPrivileges {
privilegeSet := make(map[GrantPrivilege]struct{})

// Replace any shorthand privileges with their full equivalents
for _, p := range *gp {
if _, ok := grantReplacements[p]; ok {
for _, rp := range grantReplacements[p] {
privilegeSet[rp] = struct{}{}
}
} else {
privilegeSet[p] = struct{}{}
}
}

privileges := make([]GrantPrivilege, 0, len(privilegeSet))
for p := range privilegeSet {
privileges = append(privileges, p)
}

return privileges
}

// ToStringSlice converts the slice of privileges to strings
func (gp *GrantPrivileges) ToStringSlice() []string {
if gp == nil {
Expand Down
4 changes: 3 additions & 1 deletion pkg/controller/postgresql/grant/reconciler.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,9 @@ func selectGrantQuery(gp v1alpha1.GrantParameters, q *xsql.Query) error {
return nil
case roleDatabase:
gro := gp.WithOption != nil && *gp.WithOption == v1alpha1.GrantOptionGrant
sp := gp.Privileges.ToStringSlice()

ep := gp.Privileges.ExpandPrivileges()
sp := ep.ToStringSlice()
// Join grantee. Filter by database name and grantee name.
// Finally, perform a permission comparison against expected
// permissions.
Expand Down
36 changes: 36 additions & 0 deletions pkg/controller/postgresql/grant/reconciler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"github.com/crossplane-contrib/provider-sql/apis/postgresql/v1alpha1"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/lib/pq"
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
"k8s.io/utils/pointer"
Expand Down Expand Up @@ -241,6 +242,41 @@ func TestObserve(t *testing.T) {
o: managed.ExternalObservation{ResourceExists: false},
},
},
"AllMapsToExpandedPrivileges": {
reason: "We expand ALL to CREATE, TEMPORARY, CONNECT when checking for existing grants",
fields: fields{
db: mockDB{
MockScan: func(ctx context.Context, q xsql.Query, dest ...interface{}) error {
privileges := q.Parameters[3]

// Return if there's a diff between the expected and actual privileges
diff := cmp.Diff(&pq.StringArray{"CREATE", "TEMPORARY", "CONNECT"}, privileges)

bv := dest[0].(*bool)
*bv = diff == ""

return nil
},
},
},
args: args{
mg: &v1alpha1.Grant{
Spec: v1alpha1.GrantSpec{
ForProvider: v1alpha1.GrantParameters{
Database: pointer.StringPtr("test-example"),
Role: pointer.StringPtr("test-example"),
Privileges: v1alpha1.GrantPrivileges{"ALL"},
},
},
},
},
want: want{
o: managed.ExternalObservation{
ResourceExists: true,
ResourceUpToDate: true,
},
},
},
"ErrSelectGrant": {
reason: "We should return any errors encountered while trying to show the grant",
fields: fields{
Expand Down

0 comments on commit 15aa531

Please sign in to comment.