Update and rename pgacl to postgresql-acl.
This commit is contained in:
parent
897609878f
commit
fab7bd3daf
|
@ -1,7 +0,0 @@
|
||||||
package pgacl
|
|
||||||
|
|
||||||
// ACL is a generic interface that all pgacl types must adhere to
|
|
||||||
type ACL interface {
|
|
||||||
// String creates a PostgreSQL compatible ACL string
|
|
||||||
String() string
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
package pgacl
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Schema models the privileges of a schema
|
|
||||||
type Schema struct {
|
|
||||||
Role string
|
|
||||||
Create bool
|
|
||||||
CreateGrant bool
|
|
||||||
Usage bool
|
|
||||||
UsageGrant bool
|
|
||||||
}
|
|
||||||
|
|
||||||
const numSchemaOpts = 4
|
|
||||||
|
|
||||||
// NewSchema parses a PostgreSQL ACL string for a schema and returns a Schema
|
|
||||||
// object
|
|
||||||
func NewSchema(aclStr string) (Schema, error) {
|
|
||||||
acl := Schema{}
|
|
||||||
idx := strings.IndexByte(aclStr, '=')
|
|
||||||
if idx == -1 {
|
|
||||||
return Schema{}, fmt.Errorf("invalid aclStr format: %+q", aclStr)
|
|
||||||
}
|
|
||||||
|
|
||||||
acl.Role = aclStr[:idx]
|
|
||||||
|
|
||||||
aclLen := len(aclStr)
|
|
||||||
var i int
|
|
||||||
withGrant := func() bool {
|
|
||||||
if i+1 >= aclLen {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if aclStr[i+1] == '*' {
|
|
||||||
i++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for i = idx + 1; i < aclLen; i++ {
|
|
||||||
switch aclStr[i] {
|
|
||||||
case 'C':
|
|
||||||
acl.Create = true
|
|
||||||
if withGrant() {
|
|
||||||
acl.CreateGrant = true
|
|
||||||
}
|
|
||||||
case 'U':
|
|
||||||
acl.Usage = true
|
|
||||||
if withGrant() {
|
|
||||||
acl.UsageGrant = true
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return Schema{}, fmt.Errorf("invalid byte %c in schema ACL at %d: %+q", aclStr[i], i, aclStr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return acl, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// String creates a PostgreSQL native output for the ACLs that apply to a
|
|
||||||
// schema.
|
|
||||||
func (s Schema) String() string {
|
|
||||||
b := new(bytes.Buffer)
|
|
||||||
b.Grow(len(s.Role) + numSchemaOpts + 1)
|
|
||||||
|
|
||||||
fmt.Fprint(b, s.Role, "=")
|
|
||||||
|
|
||||||
if s.Usage {
|
|
||||||
fmt.Fprint(b, "U")
|
|
||||||
if s.UsageGrant {
|
|
||||||
fmt.Fprint(b, "*")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if s.Create {
|
|
||||||
fmt.Fprint(b, "C")
|
|
||||||
if s.CreateGrant {
|
|
||||||
fmt.Fprint(b, "*")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.String()
|
|
||||||
}
|
|
|
@ -0,0 +1,270 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ACL represents a single PostgreSQL `aclitem` entry.
|
||||||
|
type ACL struct {
|
||||||
|
Privileges Privileges
|
||||||
|
GrantOptions Privileges
|
||||||
|
Role string
|
||||||
|
GrantedBy string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGrantOption returns true if the acl has the grant option set for the
|
||||||
|
// specified priviledge.
|
||||||
|
func (a ACL) GetGrantOption(priv Privileges) bool {
|
||||||
|
if a.GrantOptions&priv != 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetPriviledge returns true if the acl has the specified priviledge set.
|
||||||
|
func (a ACL) GetPrivilege(priv Privileges) bool {
|
||||||
|
if a.Privileges&priv != 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse parses a PostgreSQL aclitem string and returns an ACL
|
||||||
|
func Parse(aclStr string) (ACL, error) {
|
||||||
|
acl := ACL{}
|
||||||
|
idx := strings.IndexByte(aclStr, '=')
|
||||||
|
if idx == -1 {
|
||||||
|
return ACL{}, fmt.Errorf("invalid aclStr format: %+q", aclStr)
|
||||||
|
}
|
||||||
|
|
||||||
|
acl.Role = aclStr[:idx]
|
||||||
|
|
||||||
|
aclLen := len(aclStr)
|
||||||
|
var i int
|
||||||
|
withGrant := func() bool {
|
||||||
|
if i+1 >= aclLen {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if aclStr[i+1] == '*' {
|
||||||
|
i++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
SCAN:
|
||||||
|
for i = idx + 1; i < aclLen; i++ {
|
||||||
|
switch aclStr[i] {
|
||||||
|
case 'w':
|
||||||
|
acl.Privileges |= Update
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= Update
|
||||||
|
}
|
||||||
|
case 'r':
|
||||||
|
acl.Privileges |= Select
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= Select
|
||||||
|
}
|
||||||
|
case 'a':
|
||||||
|
acl.Privileges |= Insert
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= Insert
|
||||||
|
}
|
||||||
|
case 'd':
|
||||||
|
acl.Privileges |= Delete
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= Delete
|
||||||
|
}
|
||||||
|
case 'D':
|
||||||
|
acl.Privileges |= Truncate
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= Truncate
|
||||||
|
}
|
||||||
|
case 'x':
|
||||||
|
acl.Privileges |= References
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= References
|
||||||
|
}
|
||||||
|
case 't':
|
||||||
|
acl.Privileges |= Trigger
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= Trigger
|
||||||
|
}
|
||||||
|
case 'X':
|
||||||
|
acl.Privileges |= Execute
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= Execute
|
||||||
|
}
|
||||||
|
case 'U':
|
||||||
|
acl.Privileges |= Usage
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= Usage
|
||||||
|
}
|
||||||
|
case 'C':
|
||||||
|
acl.Privileges |= Create
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= Create
|
||||||
|
}
|
||||||
|
case 'T':
|
||||||
|
acl.Privileges |= Temporary
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= Temporary
|
||||||
|
}
|
||||||
|
case 'c':
|
||||||
|
acl.Privileges |= Connect
|
||||||
|
if withGrant() {
|
||||||
|
acl.GrantOptions |= Connect
|
||||||
|
}
|
||||||
|
case '/':
|
||||||
|
if i+1 <= aclLen {
|
||||||
|
acl.GrantedBy = aclStr[i+1:]
|
||||||
|
}
|
||||||
|
break SCAN
|
||||||
|
default:
|
||||||
|
return ACL{}, fmt.Errorf("invalid byte %c in aclitem at %d: %+q", aclStr[i], i, aclStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return acl, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// String produces a PostgreSQL aclitem-compatible string
|
||||||
|
func (a ACL) String() string {
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
bitMaskStr := permString(a.Privileges, a.GrantOptions)
|
||||||
|
role := a.Role
|
||||||
|
grantedBy := a.GrantedBy
|
||||||
|
|
||||||
|
b.Grow(len(role) + len("=") + len(bitMaskStr) + len("/") + len(grantedBy))
|
||||||
|
|
||||||
|
fmt.Fprint(b, role, "=", bitMaskStr)
|
||||||
|
|
||||||
|
if grantedBy != "" {
|
||||||
|
fmt.Fprint(b, "/", grantedBy)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// permString is a small helper function that emits the permission bitmask as a
|
||||||
|
// string.
|
||||||
|
func permString(perms, grantOptions Privileges) string {
|
||||||
|
b := new(bytes.Buffer)
|
||||||
|
b.Grow(int(numPrivileges) * 2)
|
||||||
|
|
||||||
|
// From postgresql/src/include/utils/acl.h:
|
||||||
|
//
|
||||||
|
// /* string holding all privilege code chars, in order by bitmask position */
|
||||||
|
// #define ACL_ALL_RIGHTS_STR "arwdDxtXUCTc"
|
||||||
|
if perms&Insert != 0 {
|
||||||
|
fmt.Fprint(b, "a")
|
||||||
|
if grantOptions&Insert != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perms&Select != 0 {
|
||||||
|
fmt.Fprint(b, "r")
|
||||||
|
if grantOptions&Select != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perms&Update != 0 {
|
||||||
|
fmt.Fprint(b, "w")
|
||||||
|
if grantOptions&Update != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perms&Delete != 0 {
|
||||||
|
fmt.Fprint(b, "d")
|
||||||
|
if grantOptions&Delete != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perms&Truncate != 0 {
|
||||||
|
fmt.Fprint(b, "D")
|
||||||
|
if grantOptions&Truncate != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perms&References != 0 {
|
||||||
|
fmt.Fprint(b, "x")
|
||||||
|
if grantOptions&References != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perms&Trigger != 0 {
|
||||||
|
fmt.Fprint(b, "t")
|
||||||
|
if grantOptions&Trigger != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perms&Execute != 0 {
|
||||||
|
fmt.Fprint(b, "X")
|
||||||
|
if grantOptions&Execute != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perms&Usage != 0 {
|
||||||
|
fmt.Fprint(b, "U")
|
||||||
|
if grantOptions&Usage != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perms&Create != 0 {
|
||||||
|
fmt.Fprint(b, "C")
|
||||||
|
if grantOptions&Create != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perms&Temporary != 0 {
|
||||||
|
fmt.Fprint(b, "T")
|
||||||
|
if grantOptions&Temporary != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if perms&Connect != 0 {
|
||||||
|
fmt.Fprint(b, "c")
|
||||||
|
if grantOptions&Connect != 0 {
|
||||||
|
fmt.Fprint(b, "*")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// quoteRole is a small helper function that handles the quoting of a role name,
|
||||||
|
// or PUBLIC, if no role is specified.
|
||||||
|
func quoteRole(role string) string {
|
||||||
|
if role == "" {
|
||||||
|
return "PUBLIC"
|
||||||
|
}
|
||||||
|
|
||||||
|
return pq.QuoteIdentifier(role)
|
||||||
|
}
|
||||||
|
|
||||||
|
// validRights checks to make sure a given acl's permissions and grant options
|
||||||
|
// don't exceed the specified mask valid privileges.
|
||||||
|
func validRights(acl ACL, validPrivs Privileges) bool {
|
||||||
|
if (acl.Privileges|validPrivs) == validPrivs &&
|
||||||
|
(acl.GrantOptions|validPrivs) == validPrivs {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Column models the privileges of a column aclitem
|
||||||
|
type Column struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewColumn parses an ACL object and returns a Column object.
|
||||||
|
func NewColumn(acl ACL) (Column, error) {
|
||||||
|
if !validRights(acl, validColumnPrivs) {
|
||||||
|
return Column{}, fmt.Errorf("invalid flags set for column (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validColumnPrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Column{ACL: acl}, nil
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Database models the privileges of a database aclitem
|
||||||
|
type Database struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDatabase parses an ACL object and returns a Database object.
|
||||||
|
func NewDatabase(acl ACL) (Database, error) {
|
||||||
|
if !validRights(acl, validDatabasePrivs) {
|
||||||
|
return Database{}, fmt.Errorf("invalid flags set for database (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validDatabasePrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Database{ACL: acl}, nil
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Domain models the privileges of a domain aclitem
|
||||||
|
type Domain struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewDomain parses an ACL object and returns a Domain object.
|
||||||
|
func NewDomain(acl ACL) (Domain, error) {
|
||||||
|
if !validRights(acl, validDomainPrivs) {
|
||||||
|
return Domain{}, fmt.Errorf("invalid flags set for domain (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validDomainPrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Domain{ACL: acl}, nil
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// ForeignDataWrapper models the privileges of a domain aclitem
|
||||||
|
type ForeignDataWrapper struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewForeignDataWrapper parses an ACL object and returns a ForeignDataWrapper object.
|
||||||
|
func NewForeignDataWrapper(acl ACL) (ForeignDataWrapper, error) {
|
||||||
|
if !validRights(acl, validForeignDataWrapperPrivs) {
|
||||||
|
return ForeignDataWrapper{}, fmt.Errorf("invalid flags set for domain (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validForeignDataWrapperPrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ForeignDataWrapper{ACL: acl}, nil
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// ForeignServer models the privileges of a foreign server aclitem
|
||||||
|
type ForeignServer struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewForeignServer parses an ACL object and returns a ForeignServer object.
|
||||||
|
func NewForeignServer(acl ACL) (ForeignServer, error) {
|
||||||
|
if !validRights(acl, validForeignServerPrivs) {
|
||||||
|
return ForeignServer{}, fmt.Errorf("invalid flags set for foreign server (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validForeignServerPrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ForeignServer{ACL: acl}, nil
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Function models the privileges of a function aclitem
|
||||||
|
type Function struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewFunction parses an ACL object and returns a Function object.
|
||||||
|
func NewFunction(acl ACL) (Function, error) {
|
||||||
|
if !validRights(acl, validFunctionPrivs) {
|
||||||
|
return Function{}, fmt.Errorf("invalid flags set for function (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validFunctionPrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Function{ACL: acl}, nil
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Language models the privileges of a language aclitem
|
||||||
|
type Language struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLanguage parses an ACL object and returns a Language object.
|
||||||
|
func NewLanguage(acl ACL) (Language, error) {
|
||||||
|
if !validRights(acl, validLanguagePrivs) {
|
||||||
|
return Language{}, fmt.Errorf("invalid flags set for language (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validLanguagePrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Language{ACL: acl}, nil
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// LargeObject models the privileges of a large object aclitem
|
||||||
|
type LargeObject struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLargeObject parses an ACL object and returns a LargeObject object.
|
||||||
|
func NewLargeObject(acl ACL) (LargeObject, error) {
|
||||||
|
if !validRights(acl, validLargeObjectPrivs) {
|
||||||
|
return LargeObject{}, fmt.Errorf("invalid flags set for large object (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validLargeObjectPrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return LargeObject{ACL: acl}, nil
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
// Privileges represents a PostgreSQL ACL bitmask
|
||||||
|
type Privileges uint16
|
||||||
|
|
||||||
|
// See postgresql/src/include/utils/acl.h for inspiration. Like PostgreSQL,
|
||||||
|
// "rights" refer to the combined grant option and privilege bits fields.
|
||||||
|
const (
|
||||||
|
NoPrivs Privileges = 0
|
||||||
|
|
||||||
|
// Ordering taken from postgresql/src/include/nodes/parsenodes.h
|
||||||
|
Insert Privileges = 1 << iota
|
||||||
|
Select
|
||||||
|
Update
|
||||||
|
Delete
|
||||||
|
Truncate
|
||||||
|
References
|
||||||
|
Trigger
|
||||||
|
Execute
|
||||||
|
Usage
|
||||||
|
Create
|
||||||
|
Temporary
|
||||||
|
Connect
|
||||||
|
|
||||||
|
numPrivileges
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
validColumnPrivs = Insert | Select | Update | References
|
||||||
|
validDatabasePrivs = Create | Temporary | Connect
|
||||||
|
validDomainPrivs = Usage
|
||||||
|
validForeignDataWrapperPrivs = Usage
|
||||||
|
validForeignServerPrivs = Usage
|
||||||
|
validFunctionPrivs = Execute
|
||||||
|
validLanguagePrivs = Usage
|
||||||
|
validLargeObjectPrivs = Select | Update
|
||||||
|
validSchemaPrivs = Usage | Create
|
||||||
|
validSequencePrivs = Usage | Select | Update
|
||||||
|
validTablePrivs = Insert | Select | Update | Delete | Truncate | References | Trigger
|
||||||
|
validTablespacePrivs = Create
|
||||||
|
validTypePrivs = Usage
|
||||||
|
)
|
|
@ -0,0 +1,109 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Schema models the privileges of a schema aclitem
|
||||||
|
type Schema struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSchema parses an ACL object and returns a Schema object.
|
||||||
|
func NewSchema(acl ACL) (Schema, error) {
|
||||||
|
if !validRights(acl, validSchemaPrivs) {
|
||||||
|
return Schema{}, fmt.Errorf("invalid flags set for schema (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validSchemaPrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Schema{ACL: acl}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Merge adds the argument's attributes to the receiver for values that are
|
||||||
|
// composable or not set and returns a new Schema object with the resulting
|
||||||
|
// values. Be careful with the role "" which is implicitly interpreted as the
|
||||||
|
// PUBLIC role.
|
||||||
|
func (s Schema) Merge(x Schema) Schema {
|
||||||
|
role := s.Role
|
||||||
|
if role == "" {
|
||||||
|
role = x.Role
|
||||||
|
}
|
||||||
|
|
||||||
|
grantedBy := s.GrantedBy
|
||||||
|
if grantedBy == "" {
|
||||||
|
grantedBy = x.GrantedBy
|
||||||
|
}
|
||||||
|
|
||||||
|
return Schema{
|
||||||
|
ACL{
|
||||||
|
Privileges: s.Privileges | x.Privileges,
|
||||||
|
GrantOptions: s.GrantOptions | x.GrantOptions,
|
||||||
|
Role: role,
|
||||||
|
GrantedBy: grantedBy,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grants returns a list of SQL queries that constitute the privileges specified
|
||||||
|
// in the receiver for the target schema.
|
||||||
|
func (s Schema) Grants(target string) []string {
|
||||||
|
const maxQueries = 2
|
||||||
|
queries := make([]string, 0, maxQueries)
|
||||||
|
|
||||||
|
if s.GetPrivilege(Create) {
|
||||||
|
b := bytes.NewBufferString("GRANT CREATE ON SCHEMA ")
|
||||||
|
fmt.Fprint(b, pq.QuoteIdentifier(target), " TO ", quoteRole(s.Role))
|
||||||
|
|
||||||
|
if s.GetGrantOption(Create) {
|
||||||
|
fmt.Fprint(b, " WITH GRANT OPTION")
|
||||||
|
}
|
||||||
|
|
||||||
|
queries = append(queries, b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.GetPrivilege(Usage) {
|
||||||
|
b := bytes.NewBufferString("GRANT USAGE ON SCHEMA ")
|
||||||
|
fmt.Fprint(b, pq.QuoteIdentifier(target), " TO ", quoteRole(s.Role))
|
||||||
|
|
||||||
|
if s.GetGrantOption(Usage) {
|
||||||
|
fmt.Fprint(b, " WITH GRANT OPTION")
|
||||||
|
}
|
||||||
|
|
||||||
|
queries = append(queries, b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return queries
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revokes returns a list of SQL queries that remove the privileges specified
|
||||||
|
// in the receiver from the target schema.
|
||||||
|
func (s Schema) Revokes(target string) []string {
|
||||||
|
const maxQueries = 2
|
||||||
|
queries := make([]string, 0, maxQueries)
|
||||||
|
|
||||||
|
if s.GetPrivilege(Create) {
|
||||||
|
b := bytes.NewBufferString("REVOKE")
|
||||||
|
if s.GetGrantOption(Create) {
|
||||||
|
fmt.Fprint(b, " GRANT OPTION FOR")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(b, " CREATE ON SCHEMA ")
|
||||||
|
fmt.Fprint(b, pq.QuoteIdentifier(target), " FROM ", quoteRole(s.Role))
|
||||||
|
queries = append(queries, b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if s.GetPrivilege(Usage) {
|
||||||
|
b := bytes.NewBufferString("REVOKE")
|
||||||
|
if s.GetGrantOption(Usage) {
|
||||||
|
fmt.Fprint(b, " GRANT OPTION FOR")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(b, " USAGE ON SCHEMA ")
|
||||||
|
fmt.Fprint(b, pq.QuoteIdentifier(target), " FROM ", quoteRole(s.Role))
|
||||||
|
queries = append(queries, b.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
return queries
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Sequence models the privileges of a sequence aclitem
|
||||||
|
type Sequence struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSequence parses a PostgreSQL ACL string for a sequence and returns a Sequence
|
||||||
|
// object
|
||||||
|
func NewSequence(acl ACL) (Sequence, error) {
|
||||||
|
if !validRights(acl, validSequencePrivs) {
|
||||||
|
return Sequence{}, fmt.Errorf("invalid flags set for sequence (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validSequencePrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Sequence{ACL: acl}, nil
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Table models the privileges of a table aclitem
|
||||||
|
type Table struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTable parses a PostgreSQL ACL string for a table and returns a Table
|
||||||
|
// object
|
||||||
|
func NewTable(acl ACL) (Table, error) {
|
||||||
|
if !validRights(acl, validTablePrivs) {
|
||||||
|
return Table{}, fmt.Errorf("invalid flags set for table (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validTablePrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Table{ACL: acl}, nil
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Tablespace models the privileges of a tablespace aclitem
|
||||||
|
type Tablespace struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewTablespace parses an ACL object and returns a Tablespace object.
|
||||||
|
func NewTablespace(acl ACL) (Tablespace, error) {
|
||||||
|
if !validRights(acl, validTablespacePrivs) {
|
||||||
|
return Tablespace{}, fmt.Errorf("invalid flags set for tablespace (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validTablespacePrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Tablespace{ACL: acl}, nil
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package acl
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// Type models the privileges of a type aclitem
|
||||||
|
type Type struct {
|
||||||
|
ACL
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewType parses an ACL object and returns a Type object.
|
||||||
|
func NewType(acl ACL) (Type, error) {
|
||||||
|
if !validRights(acl, validTypePrivs) {
|
||||||
|
return Type{}, fmt.Errorf("invalid flags set for type (%+q), only %+q allowed", permString(acl.Privileges, acl.GrantOptions), validTypePrivs)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Type{ACL: acl}, nil
|
||||||
|
}
|
|
@ -2271,10 +2271,10 @@
|
||||||
"revisionTime": "2016-10-27T15:40:24Z"
|
"revisionTime": "2016-10-27T15:40:24Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "fCtp8mJPTtEMkpDz8TgRtJzMYw0=",
|
"checksumSHA1": "tEKRyau4iRjmq2iwJbduD9RhN5s=",
|
||||||
"path": "github.com/sean-/pgacl",
|
"path": "github.com/sean-/postgresql-acl",
|
||||||
"revision": "3d9f301f1bf3d5b590119b28132d2e8d5d1f1a62",
|
"revision": "d10489e5d217ebe9c23470c4d0ba7081a6d1e799",
|
||||||
"revisionTime": "2016-12-15T16:51:43Z"
|
"revisionTime": "2016-12-25T12:04:19Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "BqtlwAjgFuHsVVdnw+dGSe+CKLM=",
|
"checksumSHA1": "BqtlwAjgFuHsVVdnw+dGSe+CKLM=",
|
||||||
|
|
Loading…
Reference in New Issue