Skip to content

Commit

Permalink
feat: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
oxyno-zeta committed Dec 3, 2024
1 parent 40fde77 commit 50a5de5
Show file tree
Hide file tree
Showing 7 changed files with 305 additions and 7 deletions.
16 changes: 16 additions & 0 deletions api/postgresql/v1alpha1/postgresqluserrole_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,19 @@ type PostgresqlUserRolePrivilege struct {
GeneratedSecretName string `json:"generatedSecretName"`
}

type PostgresqlUserRoleAttributes struct {
// REPLICATION attribute
// Note: This can be either true, false or null (to ignore this parameter)
Replication *bool `json:"replication,omitempty"`
// BYPASSRLS attribute
// Note: This can be either true, false or null (to ignore this parameter)
BypassRLS *bool `json:"bypassRLS,omitempty"` //nolint:tagliatelle
// CONNECTION LIMIT connlimit attribute
// Note: This can be either -1, a number or null (to ignore this parameter)
// Note: Increase your number by one because operator is using the created user to perform some operations.
ConnectionLimit *int `json:"connectionLimit,omitempty"`
}

type ModeEnum string

const ProvidedMode ModeEnum = "PROVIDED"
Expand Down Expand Up @@ -90,6 +103,9 @@ type PostgresqlUserRoleSpec struct {
// Import secret name
// +optional
ImportSecretName string `json:"importSecretName,omitempty"`
// Role attributes
// Note: Only attributes that aren't conflicting with operator are supported.
RoleAttributes *PostgresqlUserRoleAttributes `json:"roleAttributes,omitempty"`
}

type UserRoleStatusPhase string
Expand Down
35 changes: 35 additions & 0 deletions api/postgresql/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

22 changes: 22 additions & 0 deletions config/crd/bases/postgresql.easymile.com_postgresqluserroles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,28 @@ spec:
- privilege
type: object
type: array
roleAttributes:
description: |-
Role attributes
Note: Only attributes that aren't conflicting with operator are supported.
properties:
bypassRLS:
description: |-
BYPASSRLS attribute
Note: This can be either true, false or null (to ignore this parameter)
type: boolean
connectionLimit:
description: |-
CONNECTION LIMIT connlimit attribute
Note: This can be either -1, a number or null (to ignore this parameter)
Note: Increase your number by one because operator is using the created user to perform some operations.
type: integer
replication:
description: |-
REPLICATION attribute
Note: This can be either true, false or null (to ignore this parameter)
type: boolean
type: object
rolePrefix:
description: User role prefix
type: string
Expand Down
4 changes: 2 additions & 2 deletions internal/controller/postgresql/postgres/azure.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ func newAzurePG(postgres *pg) PG {
}
}

func (azpg *azurepg) CreateUserRole(role, password string) (string, error) {
returnedRole, err := azpg.pg.CreateUserRole(role, password)
func (azpg *azurepg) CreateUserRole(role, password string, attributes *RoleAttributes) (string, error) {
returnedRole, err := azpg.pg.CreateUserRole(role, password, attributes)
if err != nil {
return "", err
}
Expand Down
4 changes: 3 additions & 1 deletion internal/controller/postgresql/postgres/postgres.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ type PG interface { //nolint:interfacebloat // This is needed
CreateSchema(db, role, schema string) error
CreateExtension(db, extension string) error
CreateGroupRole(role string) error
CreateUserRole(role, password string) (string, error)
CreateUserRole(role, password string, attributes *RoleAttributes) (string, error)
AlterRoleAttributes(role string, attributes *RoleAttributes) error
GetRoleAttributes(role string) (*RoleAttributes, error)
IsRoleExist(role string) (bool, error)
RenameRole(oldname, newname string) error
UpdatePassword(role, password string) error
Expand Down
117 changes: 114 additions & 3 deletions internal/controller/postgresql/postgres/role.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ package postgres

import (
"fmt"
"strings"

"github.com/lib/pq"
)

const (
CreateGroupRoleSQLTemplate = `CREATE ROLE "%s"`
CreateUserRoleSQLTemplate = `CREATE ROLE "%s" WITH LOGIN PASSWORD '%s'`
CreateUserRoleSQLTemplate = `CREATE ROLE "%s" WITH LOGIN PASSWORD '%s' %s`
GrantRoleSQLTemplate = `GRANT "%s" TO "%s"`
GrantRoleWithAdminOptionSQLTemplate = `GRANT "%s" TO "%s" WITH ADMIN OPTION`
AlterUserSetRoleSQLTemplate = `ALTER USER "%s" SET ROLE "%s"`
Expand All @@ -21,8 +22,10 @@ const (
ReassignObjectsSQLTemplate = `REASSIGN OWNED BY "%s" TO "%s"`
IsRoleExistSQLTemplate = `SELECT 1 FROM pg_roles WHERE rolname='%s'`
RenameRoleSQLTemplate = `ALTER ROLE "%s" RENAME TO "%s"`
AlterRoleWithOptionSQLTemplate = `ALTER ROLE "%s" WITH %s`
// Source: https://dba.stackexchange.com/questions/136858/postgresql-display-role-members
GetRoleMembershipSQLTemplate = `SELECT r1.rolname as "role" FROM pg_catalog.pg_roles r JOIN pg_catalog.pg_auth_members m ON (m.member = r.oid) JOIN pg_roles r1 ON (m.roleid=r1.oid) WHERE r.rolcanlogin AND r.rolname='%s'`
GetRoleAttributesSQLTemplate = `select rolconnlimit, rolreplication, rolbypassrls FROM pg_roles WHERE rolname = '%s'`
// DO NOT TOUCH THIS
// Cannot filter on compute value so... cf line before.
GetRoleSettingsSQLTemplate = `SELECT pg_catalog.split_part(pg_catalog.unnest(setconfig), '=', 1) as parameter_type, pg_catalog.split_part(pg_catalog.unnest(setconfig), '=', 2) as parameter_value, d.datname as database FROM pg_catalog.pg_roles r JOIN pg_catalog.pg_db_role_setting c ON (c.setrole = r.oid) JOIN pg_catalog.pg_database d ON (d.oid = c.setdatabase) WHERE r.rolcanlogin AND r.rolname='%s'` //nolint:lll//Because
Expand All @@ -32,6 +35,111 @@ const (
InvalidGrantOperationErrorCode = "0LP01"
)

var (
DefaultAttributeConnectionLimit = -1
DefaultAttributeReplication = false
DefaultAttributeBypassRLS = false
)

type RoleAttributes struct {
ConnectionLimit *int
Replication *bool
BypassRLS *bool
}

func (*pg) buildAttributesString(attributes *RoleAttributes) string {
// Check nil
if attributes == nil {
return ""
}

res := make([]string, 0)

// Connection limit case
if attributes.ConnectionLimit != nil {
res = append(res, fmt.Sprintf("CONNECTION LIMIT %d", *attributes.ConnectionLimit))
}

// Replication case
if attributes.Replication != nil {
if *attributes.Replication {
res = append(res, "REPLICATION")
} else {
res = append(res, "NOREPLICATION")
}
}

// BypassRLS case
if attributes.BypassRLS != nil {
if *attributes.BypassRLS {
res = append(res, "BYPASSRLS")
} else {
res = append(res, "NOBYPASSRLS")
}
}

return strings.Join(res, " ")
}

func (c *pg) AlterRoleAttributes(role string, attributes *RoleAttributes) error {
// Build attributes str
attributesSQLStr := c.buildAttributesString(attributes)
// Check if it is empty
if attributesSQLStr == "" {
return nil
}

err := c.connect(c.defaultDatabase)
if err != nil {
return err
}

_, err = c.db.Exec(fmt.Sprintf(AlterRoleWithOptionSQLTemplate, role, attributesSQLStr))
if err != nil {
return err
}

return nil
}

func (c *pg) GetRoleAttributes(role string) (*RoleAttributes, error) {
res := &RoleAttributes{
ConnectionLimit: new(int),
Replication: new(bool),
BypassRLS: new(bool),
}

err := c.connect(c.defaultDatabase)
if err != nil {
return res, err
}

rows, err := c.db.Query(fmt.Sprintf(GetRoleAttributesSQLTemplate, role))
if err != nil {
return res, err
}

defer rows.Close()

for rows.Next() {
// Scan
err = rows.Scan(res.ConnectionLimit, res.Replication, res.BypassRLS)
// Check error
if err != nil {
return res, err
}
}

// Rows error
err = rows.Err()
// Check error
if err != nil {
return res, err
}

return res, nil
}

func (c *pg) GetRoleMembership(role string) ([]string, error) {
res := make([]string, 0)

Expand Down Expand Up @@ -91,13 +199,16 @@ func (c *pg) CreateGroupRole(role string) error {
return nil
}

func (c *pg) CreateUserRole(role, password string) (string, error) {
func (c *pg) CreateUserRole(role, password string, attributes *RoleAttributes) (string, error) {
err := c.connect(c.defaultDatabase)
if err != nil {
return "", err
}

_, err = c.db.Exec(fmt.Sprintf(CreateUserRoleSQLTemplate, role, password))
// Build attributes sql
attributesSQLStr := c.buildAttributesString(attributes)

_, err = c.db.Exec(fmt.Sprintf(CreateUserRoleSQLTemplate, role, password, attributesSQLStr))
if err != nil {
return "", err
}
Expand Down
Loading

0 comments on commit 50a5de5

Please sign in to comment.