Commit 06086f3e authored by Carson Anderson's avatar Carson Anderson

Require seq field in mongo ACls

parent 5292646d
package authz
import (
"errors"
"fmt"
"sync"
"time"
......@@ -11,6 +12,13 @@ import (
"gopkg.in/mgo.v2/bson"
)
type MongoACL []MongoACLEntry
type MongoACLEntry struct {
ACLEntry `bson:",inline"`
Seq *int
}
type ACLMongoConfig struct {
MongoConfig *mgo_session.Config `yaml:"dial_info,omitempty"`
Collection string `yaml:"collection,omitempty"`
......@@ -28,9 +36,9 @@ type aclMongoAuthorizer struct {
CacheTTL time.Duration `yaml:"cache_ttl,omitempty"`
}
// NewACLMongoAuthorizer creates a new ACL Mongo authorizer
// NewACLMongoAuthorizer creates a new ACL MongoDB authorizer
func NewACLMongoAuthorizer(c *ACLMongoConfig) (Authorizer, error) {
// Attempt to create new mongo session.
// Attempt to create new MongoDB session.
session, err := mgo_session.New(c.MongoConfig)
if err != nil {
return nil, err
......@@ -67,7 +75,7 @@ func (ma *aclMongoAuthorizer) Authorize(ai *AuthRequestInfo) ([]string, error) {
// Validate ensures that any custom config options
// in a Config are set correctly.
func (c *ACLMongoConfig) Validate(configKey string) error {
//First validate the mongo config.
//First validate the MongoDB config.
if err := c.MongoConfig.Validate(configKey); err != nil {
return err
}
......@@ -119,7 +127,7 @@ func (ma *aclMongoAuthorizer) continuouslyUpdateACLCache() {
func (ma *aclMongoAuthorizer) updateACLCache() error {
// Get ACL from MongoDB
var newACL ACL
var newACL MongoACL
// Copy our session
tmp_session := ma.session.Copy()
......@@ -128,13 +136,43 @@ func (ma *aclMongoAuthorizer) updateACLCache() error {
defer tmp_session.Close()
collection := tmp_session.DB(ma.config.MongoConfig.DialInfo.Database).C(ma.config.Collection)
err := collection.Find(bson.M{}).All(&newACL)
if err != nil {
// Create sequence index obj
index := mgo.Index{
Key: []string{"seq"},
Unique: true,
DropDups: false, // Error on duplicate key document instead of drop.
}
// Enforce a sequence index. This is fine to do frequently per the docs:
// https://godoc.org/gopkg.in/mgo.v2#Collection.EnsureIndex:
// Once EnsureIndex returns successfully, following requests for the same index
// will not contact the server unless Collection.DropIndex is used to drop the same
// index, or Session.ResetIndexCache is called.
if err := collection.EnsureIndex(index); err != nil {
return err
}
// Get all ACLs that have the required key
if err := collection.Find(bson.M{}).Sort("seq").All(&newACL); err != nil {
return err
}
glog.V(2).Infof("Number of new ACL entries from MongoDB: %d", len(newACL))
newStaticAuthorizer, err := NewACLAuthorizer(newACL)
// It is possible that the top document in the collection exists with a nil Seq.
// if that's true we pull it out of the slice and complain about it.
if len(newACL) > 0 && newACL[0].Seq == nil {
topACL := newACL[0]
return errors.New(fmt.Sprintf("Seq not set for ACL entry: %+v", topACL))
}
var retACL ACL
for _, e := range newACL {
retACL = append(retACL, e.ACLEntry)
}
newStaticAuthorizer, err := NewACLAuthorizer(retACL)
if err != nil {
return err
}
......@@ -144,7 +182,7 @@ func (ma *aclMongoAuthorizer) updateACLCache() error {
ma.staticAuthorizer = newStaticAuthorizer
ma.lock.Unlock()
glog.V(2).Infof("Got new ACL from MongoDB: %s", newACL)
glog.V(1).Infof("Installed new ACL from MongoDB (%d entries)", len(newACL))
glog.V(2).Infof("Got new ACL from MongoDB: %s", retACL)
glog.V(1).Infof("Installed new ACL from MongoDB (%d entries)", len(retACL))
return nil
}
......@@ -38,15 +38,20 @@ MongoDB quite easily. Below you can find a list of ACL entries that are ready to
be imported into MongoDB. Those ACL entries reflect what's specified in the
`example/reference.yml` file under the `acl` section (aka static ACL).
The added field of seq is used to provide a reliable order which MongoDB does not
guarantee by default, i.e. [Natural Sorting](https://docs.mongodb.org/manual/reference/method/cursor.sort/#return-natural-order).
``seq`` is a required field in all MongoDB ACL documents. Any documents without this key will be excluded. seq uniqeness is also enforced.
**reference_acl.json**
```json
{"match" : {"account" : "admin"}, "actions" : ["*"], "comment" : "Admin has full access to everything."}
{"match" : {"account" : "test", "name" : "test-*"}, "actions" : ["*"], "comment" : "User \"test\" has full access to test-* images but nothing else. (1)"}
{"match" : {"account" : "test"}, "actions" : [], "comment" : "User \"test\" has full access to test-* images but nothing else. (2)"}
{"match" : {"account" : "/.+/"}, "actions" : ["pull"], "comment" : "All logged in users can pull all images."}
{"match" : {"account" : "/.+/", "name" : "${account}/*"}, "actions" : ["*"], "comment" : "All logged in users can push all images that are in a namespace beginning with their name"}
{"match" : {"account" : "", "name" : "hello-world"}, "actions" : ["pull"], "comment" : "Anonymous users can pull \"hello-world\"."}
{"seq": 10, "match" : {"account" : "admin"}, "actions" : ["*"], "comment" : "Admin has full access to everything."}
{"seq": 20, "match" : {"account" : "test", "name" : "test-*"}, "actions" : ["*"], "comment" : "User \"test\" has full access to test-* images but nothing else. (1)"}
{"seq": 30, "match" : {"account" : "test"}, "actions" : [], "comment" : "User \"test\" has full access to test-* images but nothing else. (2)"}
{"seq": 40, "match" : {"account" : "/.+/"}, "actions" : ["pull"], "comment" : "All logged in users can pull all images."}
{"seq": 50, "match" : {"account" : "/.+/", "name" : "${account}/*"}, "actions" : ["*"], "comment" : "All logged in users can push all images that are in a namespace beginning with their name"}
{"seq": 60, "match" : {"account" : "", "name" : "hello-world"}, "actions" : ["pull"], "comment" : "Anonymous users can pull \"hello-world\"."}
```
**Note** that each document entry must span exactly one line or otherwise the
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment