Update and rename pgacl to postgresql-acl.

This commit is contained in:
Sean Chittenden 2016-12-25 04:15:37 -08:00
parent 897609878f
commit fab7bd3daf
No known key found for this signature in database
GPG Key ID: 4EBC9DC16C2E5E16
19 changed files with 631 additions and 100 deletions

View File

@ -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
}

View File

@ -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()
}

270
vendor/github.com/sean-/postgresql-acl/acl.go generated vendored Normal file
View File

@ -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
}

17
vendor/github.com/sean-/postgresql-acl/column.go generated vendored Normal file
View File

@ -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
}

17
vendor/github.com/sean-/postgresql-acl/database.go generated vendored Normal file
View File

@ -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
}

17
vendor/github.com/sean-/postgresql-acl/domain.go generated vendored Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

17
vendor/github.com/sean-/postgresql-acl/function.go generated vendored Normal file
View File

@ -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
}

17
vendor/github.com/sean-/postgresql-acl/language.go generated vendored Normal file
View File

@ -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
}

17
vendor/github.com/sean-/postgresql-acl/large_object.go generated vendored Normal file
View File

@ -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
}

42
vendor/github.com/sean-/postgresql-acl/privileges.go generated vendored Normal file
View File

@ -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
)

109
vendor/github.com/sean-/postgresql-acl/schema.go generated vendored Normal file
View File

@ -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
}

18
vendor/github.com/sean-/postgresql-acl/sequence.go generated vendored Normal file
View File

@ -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
}

18
vendor/github.com/sean-/postgresql-acl/table.go generated vendored Normal file
View File

@ -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
}

17
vendor/github.com/sean-/postgresql-acl/tablespace.go generated vendored Normal file
View File

@ -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
}

17
vendor/github.com/sean-/postgresql-acl/type.go generated vendored Normal file
View File

@ -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
}

8
vendor/vendor.json vendored
View File

@ -2271,10 +2271,10 @@
"revisionTime": "2016-10-27T15:40:24Z"
},
{
"checksumSHA1": "fCtp8mJPTtEMkpDz8TgRtJzMYw0=",
"path": "github.com/sean-/pgacl",
"revision": "3d9f301f1bf3d5b590119b28132d2e8d5d1f1a62",
"revisionTime": "2016-12-15T16:51:43Z"
"checksumSHA1": "tEKRyau4iRjmq2iwJbduD9RhN5s=",
"path": "github.com/sean-/postgresql-acl",
"revision": "d10489e5d217ebe9c23470c4d0ba7081a6d1e799",
"revisionTime": "2016-12-25T12:04:19Z"
},
{
"checksumSHA1": "BqtlwAjgFuHsVVdnw+dGSe+CKLM=",