mirror of
https://github.com/aptly-dev/aptly.git
synced 2026-05-31 04:30:44 +00:00
Uploaders rules facility: controlling who can upload .changes. #71
This commit is contained in:
@@ -0,0 +1,74 @@
|
|||||||
|
package deb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UploadersRule is single rule of format: what packages can group or key upload
|
||||||
|
type UploadersRule struct {
|
||||||
|
Condition string `json:"condition"`
|
||||||
|
Allow []string `json:"allow"`
|
||||||
|
Deny []string `json:"deny"`
|
||||||
|
CompiledCondition PackageQuery `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uploaders is configuration of restrictions for .changes file importing
|
||||||
|
type Uploaders struct {
|
||||||
|
Groups map[string][]string `json:"groups"`
|
||||||
|
Rules []UploadersRule `json:"rules"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *Uploaders) expandGroupsInternal(items []string, trail []string) []string {
|
||||||
|
result := []string{}
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
// stop infinite recursion
|
||||||
|
if utils.StrSliceHasItem(trail, item) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
group, ok := u.Groups[item]
|
||||||
|
if !ok {
|
||||||
|
result = append(result, item)
|
||||||
|
} else {
|
||||||
|
newTrail := append([]string(nil), trail...)
|
||||||
|
result = append(result, u.expandGroupsInternal(group, append(newTrail, item))...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExpandGroups expands list of keys/groups into list of keys
|
||||||
|
func (u *Uploaders) ExpandGroups(items []string) []string {
|
||||||
|
result := u.expandGroupsInternal(items, []string{})
|
||||||
|
|
||||||
|
return utils.StrSliceDeduplicate(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAllowed checks whether listed keys are allowed to upload given .changes file
|
||||||
|
func (u *Uploaders) IsAllowed(keys []utils.GpgKey, changes *Changes) bool {
|
||||||
|
for _, rule := range u.Rules {
|
||||||
|
if rule.CompiledCondition.Matches(changes) {
|
||||||
|
deny := u.ExpandGroups(rule.Deny)
|
||||||
|
for _, key := range keys {
|
||||||
|
for _, item := range deny {
|
||||||
|
if item == "*" || key.Matches(utils.GpgKey(item)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allow := u.ExpandGroups(rule.Allow)
|
||||||
|
for _, key := range keys {
|
||||||
|
for _, item := range allow {
|
||||||
|
if item == "*" || key.Matches(utils.GpgKey(item)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package deb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/smira/aptly/utils"
|
||||||
|
. "gopkg.in/check.v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UploadersSuite struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Suite(&UploadersSuite{})
|
||||||
|
|
||||||
|
func (s *UploadersSuite) TestExpandGroups(c *C) {
|
||||||
|
u := &Uploaders{
|
||||||
|
Groups: map[string][]string{
|
||||||
|
"group1": {"key1", "group2"},
|
||||||
|
"group2": {"key1", "key2", "key3", "group3"},
|
||||||
|
"group3": {},
|
||||||
|
"group4": {"key1", "group5"},
|
||||||
|
"group6": {"key1", "group8"},
|
||||||
|
"group7": {"key2", "group6"},
|
||||||
|
"group8": {"group7"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Check(u.ExpandGroups([]string{"group1"}), DeepEquals, []string{"key1", "key2", "key3"})
|
||||||
|
c.Check(u.ExpandGroups([]string{"group2"}), DeepEquals, []string{"key1", "key2", "key3"})
|
||||||
|
c.Check(u.ExpandGroups([]string{"group3"}), DeepEquals, []string{})
|
||||||
|
c.Check(u.ExpandGroups([]string{"group4"}), DeepEquals, []string{"key1", "group5"})
|
||||||
|
c.Check(u.ExpandGroups([]string{"group6"}), DeepEquals, []string{"key1", "key2"})
|
||||||
|
c.Check(u.ExpandGroups([]string{"group7"}), DeepEquals, []string{"key2", "key1"})
|
||||||
|
c.Check(u.ExpandGroups([]string{"group8"}), DeepEquals, []string{"key2", "key1"})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *UploadersSuite) TestIsAllowed(c *C) {
|
||||||
|
u := &Uploaders{
|
||||||
|
Groups: map[string][]string{
|
||||||
|
"group1": {"37E1C17570096AD1", "EC4B033C70096AD1"},
|
||||||
|
},
|
||||||
|
Rules: []UploadersRule{
|
||||||
|
{
|
||||||
|
CompiledCondition: &FieldQuery{Field: "Source", Relation: VersionEqual, Value: "calamares"},
|
||||||
|
Allow: []string{"*"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CompiledCondition: &FieldQuery{Field: "Source", Relation: VersionEqual, Value: "never-calamares"},
|
||||||
|
Deny: []string{"*"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CompiledCondition: &FieldQuery{Field: "Source", Relation: VersionEqual, Value: "some-calamares"},
|
||||||
|
Allow: []string{"group1", "12345678"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
CompiledCondition: &FieldQuery{Field: "Source", Relation: VersionEqual, Value: "some-calamares"},
|
||||||
|
Deny: []string{"45678901", "12345678"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// no keys - not allowed
|
||||||
|
c.Check(u.IsAllowed([]utils.GpgKey{}, &Changes{Stanza: Stanza{"Source": "calamares"}}), Equals, false)
|
||||||
|
|
||||||
|
// no rule - not allowed
|
||||||
|
c.Check(u.IsAllowed([]utils.GpgKey{"37E1C17570096AD1", "EC4B033C70096AD1"}, &Changes{Stanza: Stanza{"Source": "unknown-calamares"}}), Equals, false)
|
||||||
|
|
||||||
|
// first rule: allow anyone do stuff with calamares
|
||||||
|
c.Check(u.IsAllowed([]utils.GpgKey{"ABCD1234", "1234ABCD"}, &Changes{Stanza: Stanza{"Source": "calamares"}}), Equals, true)
|
||||||
|
|
||||||
|
// second rule: nobody is allowed to do stuff with never-calamares
|
||||||
|
c.Check(u.IsAllowed([]utils.GpgKey{"ABCD1234", "1234ABCD"}, &Changes{Stanza: Stanza{"Source": "never-calamares"}}), Equals, false)
|
||||||
|
|
||||||
|
// third rule: anyone from the group or explicit key
|
||||||
|
c.Check(u.IsAllowed([]utils.GpgKey{"45678901", "12345678"}, &Changes{Stanza: Stanza{"Source": "some-calamares"}}), Equals, true)
|
||||||
|
c.Check(u.IsAllowed([]utils.GpgKey{"37E1C17570096AD1"}, &Changes{Stanza: Stanza{"Source": "some-calamares"}}), Equals, true)
|
||||||
|
c.Check(u.IsAllowed([]utils.GpgKey{"70096AD1"}, &Changes{Stanza: Stanza{"Source": "some-calamares"}}), Equals, true)
|
||||||
|
|
||||||
|
// fourth rule: some are not allowed
|
||||||
|
c.Check(u.IsAllowed([]utils.GpgKey{"ABCD1234", "45678901"}, &Changes{Stanza: Stanza{"Source": "some-calamares"}}), Equals, false)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user