Skip to content

Commit 23bec13

Browse files
committed
Extend Grant support to narrow permissions to object in a Schema
1 parent 33fb91c commit 23bec13

File tree

4 files changed

+142
-6
lines changed

4 files changed

+142
-6
lines changed

apis/postgresql/v1alpha1/grant_types.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,20 @@ type GrantParameters struct {
141141
// +optional
142142
DatabaseSelector *xpv1.Selector `json:"databaseSelector,omitempty"`
143143

144+
// Schema this grant is for.
145+
// +optional
146+
Schema *string `json:"schema,omitempty"`
147+
148+
// SchemaRef references the schema object this grant it for.
149+
// +immutable
150+
// +optional
151+
SchemaRef *xpv1.Reference `json:"schemaRef,omitempty"`
152+
153+
// SchemaSelector selects a reference to a Schema this grant is for.
154+
// +immutable
155+
// +optional
156+
SchemaSelector *xpv1.Selector `json:"schemaSelector,omitempty"`
157+
144158
// MemberOf is the Role that this grant makes Role a member of.
145159
// +optional
146160
MemberOf *string `json:"memberOf,omitempty"`
@@ -212,6 +226,20 @@ func (mg *Grant) ResolveReferences(ctx context.Context, c client.Reader) error {
212226
mg.Spec.ForProvider.Database = reference.ToPtrValue(rsp.ResolvedValue)
213227
mg.Spec.ForProvider.DatabaseRef = rsp.ResolvedReference
214228

229+
// Resolve spec.forProvider.schema
230+
rsp, err = r.Resolve(ctx, reference.ResolutionRequest{
231+
CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.Schema),
232+
Reference: mg.Spec.ForProvider.SchemaRef,
233+
Selector: mg.Spec.ForProvider.SchemaSelector,
234+
To: reference.To{Managed: &Schema{}, List: &SchemaList{}},
235+
Extract: reference.ExternalName(),
236+
})
237+
if err != nil {
238+
return errors.Wrap(err, "spec.forProvider.schema")
239+
}
240+
mg.Spec.ForProvider.Schema = reference.ToPtrValue(rsp.ResolvedValue)
241+
mg.Spec.ForProvider.SchemaRef = rsp.ResolvedReference
242+
215243
// Resolve spec.forProvider.role
216244
rsp, err = r.Resolve(ctx, reference.ResolutionRequest{
217245
CurrentValue: reference.FromPtrValue(mg.Spec.ForProvider.Role),

apis/postgresql/v1alpha1/zz_generated.deepcopy.go

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package/crds/postgresql.sql.crossplane.io_grants.yaml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,84 @@ spec:
336336
type: string
337337
type: object
338338
type: object
339+
schema:
340+
description: Schema this grant is for.
341+
type: string
342+
schemaRef:
343+
description: SchemaRef references the schema object this grant
344+
it for.
345+
properties:
346+
name:
347+
description: Name of the referenced object.
348+
type: string
349+
policy:
350+
description: Policies for referencing.
351+
properties:
352+
resolution:
353+
default: Required
354+
description: Resolution specifies whether resolution of
355+
this reference is required. The default is 'Required',
356+
which means the reconcile will fail if the reference
357+
cannot be resolved. 'Optional' means this reference
358+
will be a no-op if it cannot be resolved.
359+
enum:
360+
- Required
361+
- Optional
362+
type: string
363+
resolve:
364+
description: Resolve specifies when this reference should
365+
be resolved. The default is 'IfNotPresent', which will
366+
attempt to resolve the reference only when the corresponding
367+
field is not present. Use 'Always' to resolve the reference
368+
on every reconcile.
369+
enum:
370+
- Always
371+
- IfNotPresent
372+
type: string
373+
type: object
374+
required:
375+
- name
376+
type: object
377+
schemaSelector:
378+
description: SchemaSelector selects a reference to a Schema this
379+
grant is for.
380+
properties:
381+
matchControllerRef:
382+
description: MatchControllerRef ensures an object with the
383+
same controller reference as the selecting object is selected.
384+
type: boolean
385+
matchLabels:
386+
additionalProperties:
387+
type: string
388+
description: MatchLabels ensures an object with matching labels
389+
is selected.
390+
type: object
391+
policy:
392+
description: Policies for selection.
393+
properties:
394+
resolution:
395+
default: Required
396+
description: Resolution specifies whether resolution of
397+
this reference is required. The default is 'Required',
398+
which means the reconcile will fail if the reference
399+
cannot be resolved. 'Optional' means this reference
400+
will be a no-op if it cannot be resolved.
401+
enum:
402+
- Required
403+
- Optional
404+
type: string
405+
resolve:
406+
description: Resolve specifies when this reference should
407+
be resolved. The default is 'IfNotPresent', which will
408+
attempt to resolve the reference only when the corresponding
409+
field is not present. Use 'Always' to resolve the reference
410+
on every reconcile.
411+
enum:
412+
- Always
413+
- IfNotPresent
414+
type: string
415+
type: object
416+
type: object
339417
withOption:
340418
description: |-
341419
WithOption allows an option to be set on the grant.

pkg/controller/postgresql/grant/reconciler.go

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const (
5151
errSelectGrant = "cannot select grant"
5252
errCreateGrant = "cannot create grant"
5353
errRevokeGrant = "cannot revoke grant"
54+
errNoSchema = "schema not passed or could not be resolved"
5455
errNoRole = "role not passed or could not be resolved"
5556
errNoDatabase = "database not passed or could not be resolved"
5657
errNoPrivileges = "privileges not passed"
@@ -199,29 +200,40 @@ func selectGrantQuery(gp v1alpha1.GrantParameters, q *xsql.Query) error {
199200
// permissions.
200201
q.String = "SELECT EXISTS(SELECT 1 " +
201202
"FROM pg_database db, " +
202-
"aclexplode(datacl) as acl " +
203+
"aclexplode(datacl) as acl, " +
204+
"pg_namespace as schema " +
203205
"INNER JOIN pg_roles s ON acl.grantee = s.oid " +
204206
// Filter by database, role and grantable setting
205207
"WHERE db.datname=$1 " +
206208
"AND s.rolname=$2 " +
207209
"AND acl.is_grantable=$3 " +
208-
"GROUP BY db.datname, s.rolname, acl.is_grantable " +
210+
// TODO: Filter by schema this is not working
211+
"AND schema.nspname=$4 " +
212+
"GROUP BY db.datname, s.rolname, acl.is_grantable, schema.nspname " +
209213
// Check privileges match. Convoluted right-hand-side is necessary to
210214
// ensure identical sort order of the input permissions.
211215
"HAVING array_agg(acl.privilege_type ORDER BY privilege_type ASC) " +
212-
"= (SELECT array(SELECT unnest($4::text[]) as perms ORDER BY perms ASC)))"
216+
"= (SELECT array(SELECT unnest($5::text[]) as perms ORDER BY perms ASC)))"
213217

214218
q.Parameters = []interface{}{
215219
gp.Database,
216220
gp.Role,
217221
gro,
222+
gp.Schema,
218223
pq.Array(sp),
219224
}
220225
return nil
221226
}
222227
return errors.New(errUnknownGrant)
223228
}
224229

230+
func withSchema(schema *string) string {
231+
if schema != nil {
232+
return fmt.Sprintf("IN SCHEMA %s", *schema)
233+
}
234+
return ""
235+
}
236+
225237
func withOption(option *v1alpha1.GrantOption) string {
226238
if option != nil {
227239
return fmt.Sprintf("WITH %s OPTION", string(*option))
@@ -262,17 +274,19 @@ func createGrantQueries(gp v1alpha1.GrantParameters, ql *[]xsql.Query) error { /
262274

263275
*ql = append(*ql,
264276
// REVOKE ANY MATCHING EXISTING PERMISSIONS
265-
xsql.Query{String: fmt.Sprintf("REVOKE %s ON DATABASE %s FROM %s",
277+
xsql.Query{String: fmt.Sprintf("REVOKE %s ON DATABASE %s %s FROM %s",
266278
sp,
267279
db,
280+
withSchema(gp.Schema),
268281
ro,
269282
)},
270283

271284
// GRANT REQUESTED PERMISSIONS
272-
xsql.Query{String: fmt.Sprintf("GRANT %s ON DATABASE %s TO %s %s",
285+
xsql.Query{String: fmt.Sprintf("GRANT %s ON DATABASE %s TO %s %s %s",
273286
sp,
274287
db,
275288
ro,
289+
withSchema(gp.Schema),
276290
withOption(gp.WithOption),
277291
)},
278292
)
@@ -305,9 +319,10 @@ func deleteGrantQuery(gp v1alpha1.GrantParameters, q *xsql.Query) error {
305319
)
306320
return nil
307321
case roleDatabase:
308-
q.String = fmt.Sprintf("REVOKE %s ON DATABASE %s FROM %s",
322+
q.String = fmt.Sprintf("REVOKE %s ON DATABASE %s %s FROM %s",
309323
strings.Join(gp.Privileges.ToStringSlice(), ","),
310324
pq.QuoteIdentifier(*gp.Database),
325+
withSchema(gp.Schema),
311326
ro,
312327
)
313328
return nil

0 commit comments

Comments
 (0)