-
Notifications
You must be signed in to change notification settings - Fork 59
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
K8SPG-571 grant user access to public schema #1097
base: main
Are you sure you want to change the base?
Changes from 5 commits
c2879e9
f38c4f9
7e9791e
613842f
d2a0914
0819c1b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
apiVersion: kuttl.dev/v1beta1 | ||
kind: TestStep | ||
timeout: 10 | ||
commands: | ||
- script: |- | ||
set -o errexit | ||
set -o xtrace | ||
|
||
source ../../functions | ||
|
||
kubectl -n ${NAMESPACE} patch perconapgcluster/${test_name} --type=json -p '[{"op":"add", "path":"/spec/autoCreateUserSchema","value":true},{"op":"add", "path":"/spec/users","value":[{"name":"chico","databases":["spain"],"password":{"type":"ASCII"},"secretName":"chico-credentials", "grantPublicSchemaAccess": true}]}]' | ||
sleep 10 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
apiVersion: postgres-operator.crunchydata.com/v1beta1 | ||
kind: PostgresCluster | ||
metadata: | ||
name: users | ||
ownerReferences: | ||
- apiVersion: pgv2.percona.com/v2 | ||
kind: PerconaPGCluster | ||
name: users | ||
controller: true | ||
blockOwnerDeletion: true | ||
finalizers: | ||
- postgres-operator.crunchydata.com/finalizer | ||
status: | ||
instances: | ||
- name: instance1 | ||
readyReplicas: 3 | ||
replicas: 3 | ||
updatedReplicas: 3 | ||
pgbackrest: | ||
repoHost: | ||
apiVersion: apps/v1 | ||
kind: StatefulSet | ||
ready: true | ||
repos: | ||
- bound: true | ||
name: repo1 | ||
replicaCreateBackupComplete: true | ||
stanzaCreated: true | ||
proxy: | ||
pgBouncer: | ||
readyReplicas: 3 | ||
replicas: 3 | ||
--- | ||
apiVersion: pgv2.percona.com/v2 | ||
kind: PerconaPGCluster | ||
metadata: | ||
name: users | ||
status: | ||
pgbouncer: | ||
ready: 3 | ||
size: 3 | ||
postgres: | ||
instances: | ||
- name: instance1 | ||
ready: 3 | ||
size: 3 | ||
ready: 3 | ||
size: 3 | ||
state: ready |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
apiVersion: kuttl.dev/v1beta1 | ||
kind: TestStep | ||
commands: | ||
- script: |- | ||
set -o errexit | ||
set -o xtrace | ||
|
||
source ../../functions | ||
|
||
password=$(get_psql_user_pass chico-credentials) | ||
user='chico' | ||
db_name='spain' | ||
schema='public' | ||
hostname=$(get_pgbouncer_host chico-credentials) | ||
|
||
|
||
run_psql \ | ||
'SET search_path TO public;CREATE TABLE IF NOT EXISTS customApp (id int PRIMARY KEY);' \ | ||
"-h $hostname -U $user -d $db_name" "$password" | ||
run_psql \ | ||
"INSERT INTO $schema.customApp (id) VALUES (100500)" \ | ||
"-h $hostname -U $user -d $db_name" "$password" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
apiVersion: kuttl.dev/v1beta1 | ||
kind: TestAssert | ||
timeout: 30 | ||
--- | ||
kind: ConfigMap | ||
apiVersion: v1 | ||
metadata: | ||
name: 10-read-from-primary-custom-db | ||
data: | ||
data: ' 100500' |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
apiVersion: kuttl.dev/v1beta1 | ||
kind: TestStep | ||
timeout: 30 | ||
commands: | ||
- script: |- | ||
set -o errexit | ||
set -o xtrace | ||
|
||
source ../../functions | ||
|
||
password=$(get_psql_user_pass chico-credentials) | ||
user='chico' | ||
db_name='spain' | ||
schema='public' | ||
hostname=$(get_pgbouncer_host chico-credentials) | ||
|
||
data=$(run_psql "SELECT * from $schema.customApp;" "-h $hostname -U $user -d $db_name" "$password") | ||
|
||
kubectl create configmap -n "${NAMESPACE}" 10-read-from-primary-custom-db --from-literal=data="${data}" |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -166,15 +166,28 @@ SELECT pg_catalog.format('GRANT ALL PRIVILEGES ON DATABASE %I TO %I', | |
autoCreateUserSchemaAnnotationValue, annotationExists := cluster.Annotations[naming.AutoCreateUserSchemaAnnotation] | ||
if annotationExists && strings.EqualFold(autoCreateUserSchemaAnnotationValue, "true") { | ||
log.V(1).Info("Writing schemas for users.") | ||
err = WriteUsersSchemasInPostgreSQL(ctx, exec, users) | ||
err = writeUsersSchemasInPostgreSQL(ctx, exec, users) | ||
} | ||
} | ||
|
||
for i := range users { | ||
user := users[i] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can use the way to iterate on elements of an array without using indexes:
|
||
|
||
// We skip if the user has no databases | ||
if len(user.Databases) == 0 { | ||
continue | ||
} | ||
if cluster.CompareVersion("2.7.0") >= 0 && user.GrantPublicSchemaAccess != nil && *user.GrantPublicSchemaAccess { | ||
log.V(1).Info("Granting access to public schema for user.", "name", string(user.Name)) | ||
err = grantUserAccessToPublicSchemaInPostgreSQL(ctx, exec, user) | ||
} | ||
} | ||
|
||
return err | ||
} | ||
|
||
// WriteUsersSchemasInPostgreSQL will create a schema for each user in each database that user has access to | ||
func WriteUsersSchemasInPostgreSQL(ctx context.Context, exec Executor, | ||
// writeUsersSchemasInPostgreSQL will create a schema for each user in each database that user has access to | ||
func writeUsersSchemasInPostgreSQL(ctx context.Context, exec Executor, | ||
users []v1beta1.PostgresUserSpec) error { | ||
|
||
log := logging.FromContext(ctx) | ||
|
@@ -239,3 +252,58 @@ func WriteUsersSchemasInPostgreSQL(ctx context.Context, exec Executor, | |
} | ||
return err | ||
} | ||
|
||
// grantUserAccessToPublicSchemaInPostgreSQL grant the specified user access to the public schema within the specified database. | ||
func grantUserAccessToPublicSchemaInPostgreSQL(ctx context.Context, exec Executor, | ||
user v1beta1.PostgresUserSpec) error { | ||
|
||
log := logging.FromContext(ctx) | ||
|
||
var err error | ||
var stdout string | ||
var stderr string | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why did these variables need to be defined here? We can simply do |
||
// We skip if the user has no databases | ||
if len(user.Databases) == 0 { | ||
return nil | ||
} | ||
|
||
var sql bytes.Buffer | ||
|
||
// Prevent unexpected dereferences by emptying "search_path". The "pg_catalog" | ||
// schema is still searched, and only temporary objects can be created. | ||
// - https://www.postgresql.org/docs/current/runtime-config-client.html#GUC-SEARCH-PATH | ||
_, _ = sql.WriteString(`SET search_path TO '';`) | ||
|
||
_, _ = sql.WriteString(`SELECT * FROM json_array_elements_text(:'databases');`) | ||
|
||
databases, _ := json.Marshal(user.Databases) | ||
|
||
stdout, stderr, err = exec.ExecInDatabasesFromQuery(ctx, | ||
sql.String(), | ||
strings.Join([]string{ | ||
// Quiet NOTICE messages from IF EXISTS statements. | ||
`SET client_min_messages = WARNING;`, | ||
|
||
// Grant all privileges on the public schema to the user | ||
`GRANT ALL PRIVILEGES ON SCHEMA public TO :"username";`, | ||
|
||
// Grant all privileges on existing tables and sequences in the public schema | ||
`GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO :"username";`, | ||
`GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO :"username";`, | ||
|
||
// Set default privileges for future objects created in the public schema | ||
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON TABLES TO :"username";`, | ||
`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL PRIVILEGES ON SEQUENCES TO :"username";`, | ||
}, "\n"), | ||
map[string]string{ | ||
"databases": string(databases), | ||
"username": string(user.Name), | ||
"ON_ERROR_STOP": "on", // Abort when any one statement fails. | ||
"QUIET": "on", // Do not print successful commands to stdout. | ||
}, | ||
) | ||
|
||
log.V(1).Info("grant access to public PostgreSQL schemas", "stdout", stdout, "stderr", stderr) | ||
|
||
return err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nmarukovich please confirm this name with our PM