Beispiel #1
0
Datei: vc.py Projekt: kaday/rose
 def validate_info_config(cls, info_config):
     """Validate contents in suite info file."""
     reports = DefaultValidators().validate(
         info_config,
         load_meta_config(info_config, config_type=rose.INFO_CONFIG_NAME))
     if reports:
         raise SuiteInfoError(reports)
Beispiel #2
0
    def run(self, repos, txn):
        """Apply the rule engine on transaction "txn" to repository "repos"."""

        changes = set()  # set([(status, path), ...])
        for line in self._svnlook("changed", "-t", txn, repos).splitlines():
            status, path = line.split(None, 1)
            changes.add((status, path))
        bad_changes = []
        author = None
        super_users = None
        rev_info_map = {}  # {path-id: (owner, access-list), ...}
        txn_info_map = {}

        conf = ResourceLocator.default().get_conf()
        ignores_str = conf.get_value(["rosa-svn", "ignores"], self.IGNORES)
        ignores = shlex.split(ignores_str)

        for status, path in sorted(changes):
            if any(fnmatch(path, ignore) for ignore in ignores):
                continue

            names = path.split("/", self.LEN_ID + 1)
            tail = None
            if not names[-1]:
                tail = names.pop()

            # Directories above the suites must match the ID patterns
            is_bad = False
            for name, pattern in zip(names, self.RE_ID_NAMES):
                if not re.compile(r"\A" + pattern + r"\Z").match(name):
                    is_bad = True
                    break
            if is_bad:
                bad_changes.append(BadChange(status, path))
                continue

            # Can only add directories at levels above the suites
            if len(names) < self.LEN_ID:
                if status[0] != self.ST_ADD:
                    bad_changes.append(BadChange(status, path))
                continue
            else:
                is_meta_suite = "".join(names[0:self.LEN_ID]) == "ROSIE"

            # A file at the branch level
            if len(names) == self.LEN_ID + 1 and tail is None:
                bad_changes.append(BadChange(status, path))
                continue

            # No need to check non-trunk changes
            if len(names) > self.LEN_ID and names[self.LEN_ID] != "trunk":
                continue

            # New suite should have an info file
            if status[0] == self.ST_ADD and len(names) == self.LEN_ID:
                if (self.ST_ADD, path + "trunk/") not in changes:
                    bad_changes.append(
                        BadChange(status, path, BadChange.NO_TRUNK))
                    continue
                path_trunk_info_file = path + self.TRUNK_INFO_FILE
                if ((self.ST_ADD, path_trunk_info_file) not in changes and
                    (self.ST_UPDATE, path_trunk_info_file) not in changes):
                    bad_changes.append(
                        BadChange(status, path, BadChange.NO_INFO))
                continue

            # The rest are trunk changes in a suite
            path_head = "/".join(names[0:self.LEN_ID]) + "/"
            path_tail = path[len(path_head):]

            # For meta suite, make sure keys in keys file can be parsed
            if is_meta_suite and path_tail == self.TRUNK_KNOWN_KEYS_FILE:
                out = self._svnlook("cat", "-t", txn, repos, path)
                try:
                    shlex.split(out)
                except ValueError:
                    bad_changes.append(BadChange(status, path,
                                                 BadChange.VALUE))
                    continue

            # Suite trunk information file must have an owner
            # User IDs of owner and access list must be real
            if (status not in self.ST_DELETE
                    and path_tail == self.TRUNK_INFO_FILE):
                if path_head not in txn_info_map:
                    txn_info_map[path_head] = self._get_info(
                        repos, path_head, txn)
                txn_owner, txn_access_list = self._get_access_info(
                    txn_info_map[path_head])
                if not txn_owner:
                    bad_changes.append(
                        BadChange(status, path, BadChange.NO_OWNER))
                    continue
                if self._verify_users(status, path, txn_owner, txn_access_list,
                                      bad_changes):
                    continue
                reports = DefaultValidators().validate(
                    txn_info_map[path_head],
                    load_meta_config(txn_info_map[path_head],
                                     config_type=rose.INFO_CONFIG_NAME))
                if reports:
                    reports_str = get_reports_as_text({None: reports}, path)
                    bad_changes.append(
                        BadChange(status, path, BadChange.VALUE, reports_str))
                    continue

            # Can only remove trunk information file with suite
            if status == self.ST_DELETE and path_tail == self.TRUNK_INFO_FILE:
                if (self.ST_DELETE, path_head) not in changes:
                    bad_changes.append(
                        BadChange(status, path, BadChange.NO_INFO))
                continue

            # Can only remove trunk with suite
            if status == self.ST_DELETE and path_tail == "trunk/":
                if (self.ST_DELETE, path_head) not in changes:
                    bad_changes.append(
                        BadChange(status, path, BadChange.NO_TRUNK))
                continue

            # New suite trunk: ignore the rest
            if (self.ST_ADD, path_head + "trunk/") in changes:
                continue

            # See whether author has permission to make changes
            if author is None:
                author = self._svnlook("author", "-t", txn, repos).strip()
            if super_users is None:
                super_users = []
                for s_key in ["rosa-svn", "rosa-svn-pre-commit"]:
                    value = conf.get_value([s_key, "super-users"])
                    if value is not None:
                        super_users = shlex.split(value)
                        break
            if path_head not in rev_info_map:
                rev_info_map[path_head] = self._get_info(repos, path_head)
            owner, access_list = self._get_access_info(rev_info_map[path_head])
            admin_users = super_users + [owner]

            # Only admin users can remove the suite
            if author not in admin_users and not path_tail:
                bad_changes.append(BadChange(status, path))
                continue

            # Admin users and those in access list can modify everything in
            # trunk apart from specific entries in the trunk info file
            if "*" in access_list or author in admin_users + access_list:
                if path_tail != self.TRUNK_INFO_FILE:
                    continue
            else:
                bad_changes.append(BadChange(status, path))
                continue

            # The owner must not be deleted
            if path_head not in txn_info_map:
                txn_info_map[path_head] = self._get_info(repos, path_head, txn)
            txn_owner, txn_access_list = self._get_access_info(
                txn_info_map[path_head])
            if not txn_owner:
                bad_changes.append(BadChange(status, path, BadChange.NO_OWNER))
                continue

            # Only the admin users can change owner and access list
            if owner == txn_owner and access_list == txn_access_list:
                continue
            if author not in admin_users:
                if owner != txn_owner:
                    bad_changes.append(
                        BadChange(status, path, BadChange.PERM,
                                  "owner=" + txn_owner))
                else:  # access list
                    bad_change = BadChange(
                        status, path, BadChange.PERM,
                        "access-list=" + " ".join(txn_access_list))
                    bad_changes.append(bad_change)
                continue

        if bad_changes:
            raise BadChanges(bad_changes)