src/mercurialserver/ruleset.py
author David Douard <david.douard@logilab.fr>
Mon, 03 Nov 2014 11:12:35 +0100
changeset 371 e9ce904b62a9
parent 311 3cbde66305e4
child 372 80f78674c56e
permissions -rw-r--r--
[test] add unit tests for ruleset We extract the rules building logic from readfile into a Ruleset.buildrules() method to ease testing.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
242
03d8f07230b3 Tidy up file prologues; move credits to CREDITS
Paul Crowley <paul@lshift.net>
parents: 237
diff changeset
     1
"""
03d8f07230b3 Tidy up file prologues; move credits to CREDITS
Paul Crowley <paul@lshift.net>
parents: 237
diff changeset
     2
Glob-based, order-based rules matcher that can answer "maybe"
03d8f07230b3 Tidy up file prologues; move credits to CREDITS
Paul Crowley <paul@lshift.net>
parents: 237
diff changeset
     3
where the inputs make clear that something is unknown.
03d8f07230b3 Tidy up file prologues; move credits to CREDITS
Paul Crowley <paul@lshift.net>
parents: 237
diff changeset
     4
"""
18
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
     5
32
4059dbe9f26a new break-in system
Paul Crowley <paul@lshift.net>
parents: 23
diff changeset
     6
import sys
18
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
     7
import re
39
f5055ce263c7 New system. No breaking in, just putting files in /etc/mercurial-server
Paul Crowley <paul@lshift.net>
parents: 33
diff changeset
     8
import os
f5055ce263c7 New system. No breaking in, just putting files in /etc/mercurial-server
Paul Crowley <paul@lshift.net>
parents: 33
diff changeset
     9
import os.path
18
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    10
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    11
def globmatcher(pattern):
106
0519745e7a57 Much less strict about most things
Paul Crowley <paul@lshift.net>
parents: 78
diff changeset
    12
    p = "[^/]*".join(re.escape(c) for c in pattern.split("*"))
0519745e7a57 Much less strict about most things
Paul Crowley <paul@lshift.net>
parents: 78
diff changeset
    13
    # ** means "match recursively" ie "ignore directories"
237
d30f3f312ece Handle maybe matches properly
Paul Crowley <paul@lshift.net>
parents: 109
diff changeset
    14
    return re.compile(p.replace("[^/]*[^/]*", ".*") + "$")
18
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    15
300
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    16
# Returns 1 for a definite match
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    17
# -1 for a definite non-match
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    18
# 0 where we can't be sure because a key is None
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    19
def rmatch(k, m, kw):
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    20
    if k not in kw:
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    21
        return -1
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    22
    kkw = kw[k]
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    23
    if kkw is None:
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    24
        return 0
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    25
    elif m.match(kkw) is None:
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    26
        return -1
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    27
    else:
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    28
        return 1
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    29
18
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    30
def rule(pairs):
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    31
    matchers = [(k, globmatcher(v)) for k, v in pairs]
48
f0cb7ad9e4ab Don't use keyword arguments everywhere
Paul Crowley <paul@lshift.net>
parents: 45
diff changeset
    32
    def c(kw):
300
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    33
        return min(rmatch(k, m, kw) for k, m in matchers)
371
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    34
    c.patterns = [(k, m.pattern) for k, m in matchers]
18
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    35
    return c
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    36
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    37
class Ruleset(object):
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    38
    '''Class representing the rules in a rule file'''
311
3cbde66305e4 Fix white space
Paul Crowley <paul@lshift.net>
parents: 301
diff changeset
    39
18
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    40
    levels = ["init", "write", "read", "deny"]
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    41
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    42
    def __init__(self):
48
f0cb7ad9e4ab Don't use keyword arguments everywhere
Paul Crowley <paul@lshift.net>
parents: 45
diff changeset
    43
        self.rules = []
21
59540181a4bb simplify by allowing some params to be preset in rules
Paul Crowley <paul@ciphergoth.org>
parents: 18
diff changeset
    44
        self.preset = {}
18
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    45
21
59540181a4bb simplify by allowing some params to be preset in rules
Paul Crowley <paul@ciphergoth.org>
parents: 18
diff changeset
    46
    def set(self, **kw):
59540181a4bb simplify by allowing some params to be preset in rules
Paul Crowley <paul@ciphergoth.org>
parents: 18
diff changeset
    47
        self.preset.update(kw)
311
3cbde66305e4 Fix white space
Paul Crowley <paul@lshift.net>
parents: 301
diff changeset
    48
78
2a3407a14654 Replaced env vars with Python globals
Paul Crowley <paul@lshift.net>
parents: 77
diff changeset
    49
    def get(self, k):
2a3407a14654 Replaced env vars with Python globals
Paul Crowley <paul@lshift.net>
parents: 77
diff changeset
    50
        return self.preset.get(k, None)
300
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    51
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    52
    def allow(self, level, **kw):
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    53
        levelindex = self.levels.index(level)
21
59540181a4bb simplify by allowing some params to be preset in rules
Paul Crowley <paul@ciphergoth.org>
parents: 18
diff changeset
    54
        d = self.preset.copy()
48
f0cb7ad9e4ab Don't use keyword arguments everywhere
Paul Crowley <paul@lshift.net>
parents: 45
diff changeset
    55
        d.update(kw)
18
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    56
        for a, c in self.rules:
237
d30f3f312ece Handle maybe matches properly
Paul Crowley <paul@lshift.net>
parents: 109
diff changeset
    57
            m = c(d)
300
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    58
            if m == 1:
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    59
                # Definite match - what it says goes
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    60
                return a <= levelindex
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    61
            elif m == 0:
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    62
                # "Maybe match" - allow if it says yes, ignore if no
31c2c6b383fd Refactor ruleset.py so I can be more confident it's correct
Paul Crowley <paul@lshift.net>
parents: 297
diff changeset
    63
                if a <= levelindex:
237
d30f3f312ece Handle maybe matches properly
Paul Crowley <paul@lshift.net>
parents: 109
diff changeset
    64
                    return True
d30f3f312ece Handle maybe matches properly
Paul Crowley <paul@lshift.net>
parents: 109
diff changeset
    65
        return False
311
3cbde66305e4 Fix white space
Paul Crowley <paul@lshift.net>
parents: 301
diff changeset
    66
39
f5055ce263c7 New system. No breaking in, just putting files in /etc/mercurial-server
Paul Crowley <paul@lshift.net>
parents: 33
diff changeset
    67
    def readfile(self, fn):
237
d30f3f312ece Handle maybe matches properly
Paul Crowley <paul@lshift.net>
parents: 109
diff changeset
    68
        f = open(fn)
18
538d6b198f4a Big change to support file conditions; format of hg-ssh-access.conf
Paul Crowley <paul@lshift.net>
parents:
diff changeset
    69
        try:
371
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    70
            self.buildrules(f)
237
d30f3f312ece Handle maybe matches properly
Paul Crowley <paul@lshift.net>
parents: 109
diff changeset
    71
        finally:
d30f3f312ece Handle maybe matches properly
Paul Crowley <paul@lshift.net>
parents: 109
diff changeset
    72
            f.close()
32
4059dbe9f26a new break-in system
Paul Crowley <paul@lshift.net>
parents: 23
diff changeset
    73
371
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    74
    def buildrules(self, f):
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    75
        """Build rules from f
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    76
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    77
        f shoud be iterable per line, each line is like:
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    78
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    79
        level [user=pattern] [repo=pattern] [file=pattern] [branch=pattern]
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    80
        """
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    81
        for l in f:
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    82
            l = l.strip()
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    83
            if not l or l.startswith("#"):
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    84
                continue
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    85
            l = l.split()
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    86
            # Unrecognized actions are off the high end
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    87
            if l[0] in self.levels:
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    88
                ix = self.levels.index(l[0])
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    89
            else:
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    90
                ix = len(self.levels)
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    91
            self.rules.append((ix,
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    92
                rule([c.split("=", 1) for c in l[1:]])))
e9ce904b62a9 [test] add unit tests for ruleset
David Douard <david.douard@logilab.fr>
parents: 311
diff changeset
    93
77
8d14aac93b5d Most of the way through abolishing env vars
Paul Crowley <paul@lshift.net>
parents: 67
diff changeset
    94
rules = Ruleset()