aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--acl/acl.go21
-rw-r--r--objects/object_tree.go114
-rw-r--r--objects/object_tree_test.go59
-rw-r--r--objects/properties.go4
4 files changed, 175 insertions, 23 deletions
diff --git a/acl/acl.go b/acl/acl.go
index 544edde..d3ab912 100644
--- a/acl/acl.go
+++ b/acl/acl.go
@@ -31,6 +31,20 @@ func (p Perm) String() string {
type QualifiedPerms map[string]Perm
+func (a QualifiedPerms) Equals(b QualifiedPerms) bool {
+ if len(a) != len(b) {
+ return false
+ }
+
+ for k, va := range a {
+ if vb, ok := b[k]; !ok || vb != va {
+ return false
+ }
+ }
+
+ return true
+}
+
type ACL struct {
User, Group, Other, Mask QualifiedPerms
}
@@ -132,3 +146,10 @@ func ParseACL(s string) (ACL, error) {
return acl, nil
}
+
+func (a ACL) Equals(b ACL) bool {
+ return a.User.Equals(b.User) &&
+ a.Group.Equals(b.Group) &&
+ a.Other.Equals(b.Other) &&
+ a.Mask.Equals(b.Mask)
+}
diff --git a/objects/object_tree.go b/objects/object_tree.go
index 07c265c..2a8f982 100644
--- a/objects/object_tree.go
+++ b/objects/object_tree.go
@@ -3,6 +3,7 @@ package objects
import (
"bufio"
"bytes"
+ "code.laria.me/petrific/acl"
"errors"
"fmt"
"sort"
@@ -24,6 +25,9 @@ var treeEntryTypes = map[TreeEntryType]struct{}{
type TreeEntry interface {
Type() TreeEntryType
+ ACL() acl.ACL
+ User() string
+ Group() string
equalContent(TreeEntry) bool
toProperties() properties
}
@@ -32,49 +36,93 @@ func compareTreeEntries(a, b TreeEntry) bool {
return a.Type() == b.Type() && a.equalContent(b)
}
-type TreeEntryFile ObjectId
+type TreeEntryBase struct {
+ acl acl.ACL
+ user, group string
+}
+
+func (teb TreeEntryBase) ACL() acl.ACL {
+ return teb.acl
+}
+
+func (teb TreeEntryBase) User() string {
+ return teb.user
+}
+
+func (teb TreeEntryBase) Group() string {
+ return teb.group
+}
+
+func (teb TreeEntryBase) toProperties() properties {
+ return properties{
+ "acl": teb.acl.String(),
+ "user": teb.user,
+ "group": teb.group,
+ }
+}
+
+func (a TreeEntryBase) equalContent(b TreeEntryBase) bool {
+ return a.acl.Equals(b.acl) && a.user == b.user && a.group == b.group
+}
+
+type TreeEntryFile struct {
+ TreeEntryBase
+ Ref ObjectId
+}
func (tef TreeEntryFile) Type() TreeEntryType {
return TETFile
}
func (tef TreeEntryFile) toProperties() properties {
- return properties{"ref": ObjectId(tef).String()}
+ props := tef.TreeEntryBase.toProperties()
+ props["ref"] = tef.Ref.String()
+ return props
}
func (a TreeEntryFile) equalContent(_b TreeEntry) bool {
b, ok := _b.(TreeEntryFile)
- return ok && ObjectId(b).Equals(ObjectId(a))
+ return ok && a.TreeEntryBase.equalContent(b.TreeEntryBase) && a.Ref.Equals(b.Ref)
}
-type TreeEntryDir ObjectId
+type TreeEntryDir struct {
+ TreeEntryBase
+ Ref ObjectId
+}
func (ted TreeEntryDir) Type() TreeEntryType {
return TETDir
}
func (ted TreeEntryDir) toProperties() properties {
- return properties{"ref": ObjectId(ted).String()}
+ props := ted.TreeEntryBase.toProperties()
+ props["ref"] = ted.Ref.String()
+ return props
}
func (a TreeEntryDir) equalContent(_b TreeEntry) bool {
b, ok := _b.(TreeEntryDir)
- return ok && ObjectId(b).Equals(ObjectId(a))
+ return ok && a.TreeEntryBase.equalContent(b.TreeEntryBase) && a.Ref.Equals(b.Ref)
}
-type TreeEntrySymlink string
+type TreeEntrySymlink struct {
+ TreeEntryBase
+ Target string
+}
func (tes TreeEntrySymlink) Type() TreeEntryType {
return TETSymlink
}
func (tes TreeEntrySymlink) toProperties() properties {
- return properties{"target": string(tes)}
+ props := tes.TreeEntryBase.toProperties()
+ props["target"] = tes.Target
+ return props
}
func (a TreeEntrySymlink) equalContent(_b TreeEntry) bool {
b, ok := _b.(TreeEntrySymlink)
- return ok && b == a
+ return ok && a.TreeEntryBase.equalContent(b.TreeEntryBase) && a.Target == b.Target
}
type Tree map[string]TreeEntry
@@ -117,6 +165,28 @@ func getObjectIdFromProps(p properties, key string) (ObjectId, error) {
return oid, err
}
+func defaultFileTreeEntryBase(_acl *acl.ACL, props properties) (base TreeEntryBase) {
+ base.user = props["user"]
+ base.group = props["group"]
+ if _acl == nil {
+ base.acl = acl.ACLFromUnixPerms(0664)
+ } else {
+ base.acl = *_acl
+ }
+ return
+}
+
+func defaultDirTreeEntryBase(_acl *acl.ACL, props properties) (base TreeEntryBase) {
+ base.user = props["user"]
+ base.group = props["group"]
+ if _acl == nil {
+ base.acl = acl.ACLFromUnixPerms(0775)
+ } else {
+ base.acl = *_acl
+ }
+ return
+}
+
func (t Tree) FromPayload(payload []byte) error {
sc := bufio.NewScanner(bytes.NewReader(payload))
@@ -128,7 +198,16 @@ func (t Tree) FromPayload(payload []byte) error {
props := make(properties)
if err := props.UnmarshalText(line); err != nil {
- return nil
+ return err
+ }
+
+ var _acl *acl.ACL
+ if acl_string, ok := props["acl"]; ok {
+ acltmp, err := acl.ParseACL(acl_string)
+ if err != nil {
+ return err
+ }
+ _acl = &acltmp
}
entry_type, ok := props["type"]
@@ -143,19 +222,28 @@ func (t Tree) FromPayload(payload []byte) error {
if err != nil {
return err
}
- entry = TreeEntryFile(ref)
+ entry = TreeEntryFile{
+ TreeEntryBase: defaultFileTreeEntryBase(_acl, props),
+ Ref: ref,
+ }
case TETDir:
ref, err := getObjectIdFromProps(props, "ref")
if err != nil {
return err
}
- entry = TreeEntryDir(ref)
+ entry = TreeEntryDir{
+ TreeEntryBase: defaultDirTreeEntryBase(_acl, props),
+ Ref: ref,
+ }
case TETSymlink:
target, ok := props["target"]
if !ok {
return errors.New("Missing key: target")
}
- entry = TreeEntrySymlink(target)
+ entry = TreeEntrySymlink{
+ TreeEntryBase: defaultFileTreeEntryBase(_acl, props),
+ Target: target,
+ }
default:
// TODO: Or should we just ignore this entry? There might be more types in the future...
return fmt.Errorf("Unknown tree entry type: %s", entry_type)
diff --git a/objects/object_tree_test.go b/objects/object_tree_test.go
index c80c2e1..3ec9e25 100644
--- a/objects/object_tree_test.go
+++ b/objects/object_tree_test.go
@@ -2,22 +2,65 @@ package objects
import (
"bytes"
+ "code.laria.me/petrific/acl"
"testing"
)
var (
testTreeObj = Tree{
- "foo": TreeEntryFile(genId(0x11)),
- "bar": TreeEntryDir(genId(0x22)),
- "baz": TreeEntrySymlink("/föö&bär/💾"), // Test special chars and unicode
- "😃": TreeEntryFile(genId(0x33)),
+ "foo": TreeEntryFile{
+ TreeEntryBase: TreeEntryBase{
+ acl: acl.ACLFromUnixPerms(0644),
+ user: "user1",
+ group: "group1",
+ },
+ Ref: genId(0x11),
+ },
+ "bar": TreeEntryDir{
+ TreeEntryBase: TreeEntryBase{
+ acl: acl.ACLFromUnixPerms(0755),
+ user: "user2",
+ group: "group2",
+ },
+ Ref: genId(0x22),
+ },
+ "baz": TreeEntrySymlink{
+ TreeEntryBase: TreeEntryBase{
+ acl: acl.ACLFromUnixPerms(0644),
+ user: "user3",
+ group: "group3",
+ },
+ Target: "/föö&bär/💾",
+ }, // Test special chars and unicode
+ "😃": TreeEntryFile{
+ TreeEntryBase: TreeEntryBase{
+ acl: acl.ACL{
+ User: acl.QualifiedPerms{
+ "": acl.PermR | acl.PermW,
+ "user1": acl.PermR | acl.PermW,
+ },
+ Group: acl.QualifiedPerms{
+ "": acl.PermR,
+ },
+ Other: acl.QualifiedPerms{
+ "": acl.PermR,
+ },
+ Mask: acl.QualifiedPerms{
+ "": acl.PermR | acl.PermW,
+ },
+ },
+ user: "user4",
+ group: "group4",
+ },
+ Ref: genId(0x33),
+ },
}
testTreeSerialization = []byte("" +
- "name=%f0%9f%98%83&ref=sha3-256:3333333333333333333333333333333333333333333333333333333333333333&type=file\n" +
- "name=bar&ref=sha3-256:2222222222222222222222222222222222222222222222222222222222222222&type=dir\n" +
- "name=baz&target=%2ff%c3%b6%c3%b6%26b%c3%a4r%2f%f0%9f%92%be&type=symlink\n" +
- "name=foo&ref=sha3-256:1111111111111111111111111111111111111111111111111111111111111111&type=file\n")
+ "acl=u::rw-,g::r--,o::r--&group=group1&name=foo&ref=sha3-256:1111111111111111111111111111111111111111111111111111111111111111&type=file&user=user1\n" +
+ "acl=u::rw-,g::r--,o::r--&group=group3&name=baz&target=%2ff%c3%b6%c3%b6%26b%c3%a4r%2f%f0%9f%92%be&type=symlink&user=user3\n" +
+ "acl=u::rw-,u:user1:rw-,g::r--,o::r--,m::rw-&group=group4&name=%f0%9f%98%83&ref=sha3-256:3333333333333333333333333333333333333333333333333333333333333333&type=file&user=user4\n" +
+ "acl=u::rwx,g::r-x,o::r-x&group=group2&name=bar&ref=sha3-256:2222222222222222222222222222222222222222222222222222222222222222&type=dir&user=user2\n")
)
func TestSerializeTree(t *testing.T) {
diff --git a/objects/properties.go b/objects/properties.go
index 6b4acc1..8c92932 100644
--- a/objects/properties.go
+++ b/objects/properties.go
@@ -11,14 +11,14 @@ import (
// (only the characters [a-zA-Z0-9.:_-] are allowed, values are ordered by their key)
type properties map[string]string
-// escapePropertyString escapes all bytes not in [a-zA-Z0-9.:_-] as %XX, where XX represents the hexadecimal value of the byte.
+// escapePropertyString escapes all bytes not in [a-zA-Z0-9.,:_-] as %XX, where XX represents the hexadecimal value of the byte.
// Compatible with URL query strings
func escapePropertyString(s string) []byte {
out := []byte{}
esc := []byte("%XX")
for _, b := range []byte(s) {
- if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '.' || b == ':' || b == '_' || b == '-' {
+ if (b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '.' || b == ',' || b == ':' || b == '_' || b == '-' {
out = append(out, b)
} else {
hex.Encode(esc[1:], []byte{b})