"Fossies" - the Fresh Open Source Software Archive

Member "pyzor-1.0.0/pyzor/config.py" (10 Dec 2014, 9167 Bytes) of package /linux/privat/pyzor-1.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "config.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 0.9.0_vs_1.0.0.

    1 """Functions that handle parsing pyzor configuration files."""
    2 
    3 import os
    4 import re
    5 import logging
    6 import collections
    7 
    8 try:
    9     from raven.handlers.logging import SentryHandler
   10     _has_raven = True
   11 except ImportError:
   12     _has_raven = False
   13 
   14 import pyzor.account
   15 
   16 _COMMENT_P = re.compile(r"((?<=[^\\])#.*)")
   17 
   18 
   19 # Configuration files for the Pyzor Server
   20 def load_access_file(access_fn, accounts):
   21     """Load the ACL from the specified file, if it exists, and return an
   22     ACL dictionary, where each key is a username and each value is a set
   23     of allowed permissions (if the permission is not in the set, then it
   24     is not allowed).
   25 
   26     'accounts' is a dictionary of accounts that exist on the server - only
   27     the keys are used, which must be the usernames (these are the users
   28     that are granted permission when the 'all' keyword is used, as
   29     described below).
   30 
   31     Each line of the file should be in the following format:
   32         operation : user : allow|deny
   33     where 'operation' is a space-separated list of pyzor commands or the
   34     keyword 'all' (meaning all commands), 'username' is a space-separated
   35     list of usernames or the keyword 'all' (meaning all users) - the
   36     anonymous user is called "anonymous", and "allow|deny" indicates whether
   37     or not the specified user(s) may execute the specified operations.
   38 
   39     The file is processed from top to bottom, with the final match for
   40     user/operation being the value taken.  Every file has the following
   41     implicit final rule:
   42         all : all : deny
   43 
   44     If the file does not exist, then the following default is used:
   45         check report ping info : anonymous : allow
   46     """
   47     log = logging.getLogger("pyzord")
   48     # A defaultdict is safe, because if we get a non-existant user, we get
   49     # the empty set, which is the same as a deny, which is the final
   50     # implicit rule.
   51     acl = collections.defaultdict(set)
   52     if not os.path.exists(access_fn):
   53         log.info("Using default ACL: the anonymous user may use the check, "
   54                  "report, ping and info commands.")
   55         acl[pyzor.anonymous_user] = set(("check", "report", "ping", "pong",
   56                                          "info"))
   57         return acl
   58     accessf = open(access_fn)
   59     for line in accessf:
   60         if not line.strip() or line[0] == "#":
   61             continue
   62         try:
   63             operations, users, allowed = [part.lower().strip()
   64                                           for part in line.split(":")]
   65         except ValueError:
   66             log.warn("Invalid ACL line: %r", line)
   67             continue
   68         try:
   69             allowed = {"allow": True, "deny": False}[allowed]
   70         except KeyError:
   71             log.warn("Invalid ACL line: %r", line)
   72             continue
   73         if operations == "all":
   74             operations = ("check", "report", "ping", "pong", "info",
   75                           "whitelist")
   76         else:
   77             operations = [operation.strip()
   78                           for operation in operations.split()]
   79         if users == "all":
   80             users = accounts
   81         else:
   82             users = [user.strip() for user in users.split()]
   83         for user in users:
   84             if allowed:
   85                 log.debug("Granting %s to %s.", ",".join(operations), user)
   86                 # If these operations are already allowed, this will have
   87                 # no effect.
   88                 acl[user].update(operations)
   89             else:
   90                 log.debug("Revoking %s from %s.", ",".join(operations), user)
   91                 # If these operations are not allowed yet, this will have
   92                 # no effect.
   93                 acl[user].difference_update(operations)
   94     accessf.close()
   95     log.info("ACL: %r", acl)
   96     return acl
   97 
   98 
   99 def load_passwd_file(passwd_fn):
  100     """Load the accounts from the specified file.
  101 
  102     Each line of the file should be in the format:
  103         username : key
  104 
  105     If the file does not exist, then an empty dictionary is returned;
  106     otherwise, a dictionary of (username, key) items is returned.
  107     """
  108     log = logging.getLogger("pyzord")
  109     accounts = {}
  110     if not os.path.exists(passwd_fn):
  111         log.info("Accounts file does not exist - only the anonymous user "
  112                  "will be available.")
  113         return accounts
  114     passwdf = open(passwd_fn)
  115     for line in passwdf:
  116         if not line.strip() or line[0] == "#":
  117             continue
  118         try:
  119             user, key = line.split(":")
  120         except ValueError:
  121             log.warn("Invalid accounts line: %r", line)
  122             continue
  123         user = user.strip()
  124         key = key.strip()
  125         log.debug("Creating an account for %s with key %s.", user, key)
  126         accounts[user] = key
  127     passwdf.close()
  128     # Don't log the keys at 'info' level, just ther usernames.
  129     log.info("Accounts: %s", ",".join(accounts))
  130     return accounts
  131 
  132 
  133 # Configuration files for the Pyzor Client
  134 def load_accounts(filepath):
  135     """Layout of file is: host : port : username : salt,key"""
  136     accounts = {}
  137     log = logging.getLogger("pyzor")
  138     if os.path.exists(filepath):
  139         accountsf = open(filepath)
  140         for lineno, orig_line in enumerate(accountsf):
  141             line = orig_line.strip()
  142             if not line or line.startswith('#'):
  143                 continue
  144             try:
  145                 host, port, username, key = [x.strip()
  146                                              for x in line.split(":")]
  147             except ValueError:
  148                 log.warn("account file: invalid line %d: wrong number of "
  149                          "parts", lineno)
  150                 continue
  151             try:
  152                 port = int(port)
  153             except ValueError as ex:
  154                 log.warn("account file: invalid line %d: %s", lineno, ex)
  155                 continue
  156             address = (host, port)
  157             try:
  158                 salt, key = pyzor.account.key_from_hexstr(key)
  159             except ValueError as ex:
  160                 log.warn("account file: invalid line %d: %s", lineno, ex)
  161                 continue
  162             if not salt and not key:
  163                 log.warn("account file: invalid line %d: keystuff can't be "
  164                          "all None's", lineno)
  165                 continue
  166             accounts[address] = pyzor.account.Account(username, salt, key)
  167         accountsf.close()
  168 
  169     else:
  170         log.warn("No accounts are setup.  All commands will be executed by "
  171                  "the anonymous user.")
  172     return accounts
  173 
  174 
  175 def load_servers(filepath):
  176     """Load the servers file."""
  177     logger = logging.getLogger("pyzor")
  178     if not os.path.exists(filepath):
  179         servers = []
  180     else:
  181         servers = []
  182         with open(filepath) as serverf:
  183             for line in serverf:
  184                 line = line.strip()
  185                 if re.match("[^#][a-zA-Z0-9.-]+:[0-9]+", line):
  186                     address, port = line.rsplit(":", 1)
  187                     servers.append((address, int(port)))
  188 
  189     if not servers:
  190         logger.info("No servers specified, defaulting to public.pyzor.org.")
  191         servers = [("public.pyzor.org", 24441)]
  192     return servers
  193 
  194 
  195 def load_local_whitelist(filepath):
  196     """Load the local digest skip file."""
  197     if not os.path.exists(filepath):
  198         return set()
  199 
  200     whitelist = set()
  201     with open(filepath) as serverf:
  202         for line in serverf:
  203             # Remove any comments
  204             line = _COMMENT_P.sub("", line).strip()
  205             if line:
  206                 whitelist.add(line)
  207     return whitelist
  208 
  209 
  210 # Common configurations
  211 def setup_logging(log_name, filepath, debug, sentry_dsn=None,
  212                   sentry_lvl="WARN"):
  213     """Setup logging according to the specified options. Return the Logger
  214     object.
  215     """
  216     fmt = logging.Formatter('%(asctime)s (%(process)d) %(levelname)s '
  217                             '%(message)s')
  218 
  219     stream_handler = logging.StreamHandler()
  220 
  221     if debug:
  222         stream_log_level = logging.DEBUG
  223         file_log_level = logging.DEBUG
  224     else:
  225         stream_log_level = logging.CRITICAL
  226         file_log_level = logging.INFO
  227 
  228     logger = logging.getLogger(log_name)
  229     logger.setLevel(file_log_level)
  230 
  231     stream_handler.setLevel(stream_log_level)
  232     stream_handler.setFormatter(fmt)
  233     logger.addHandler(stream_handler)
  234 
  235     if filepath:
  236         file_handler = logging.FileHandler(filepath)
  237         file_handler.setLevel(file_log_level)
  238         file_handler.setFormatter(fmt)
  239         logger.addHandler(file_handler)
  240 
  241     if sentry_dsn and _has_raven:
  242         sentry_level = getattr(logging, sentry_lvl)
  243         sentry_handler = SentryHandler(sentry_dsn)
  244         sentry_handler.setLevel(sentry_level)
  245         logger.addHandler(sentry_handler)
  246 
  247     return logger
  248 
  249 
  250 def expand_homefiles(homefiles, category, homedir, config):
  251     """Set the full file path for these configuration files."""
  252     for filename in homefiles:
  253         filepath = config.get(category, filename)
  254         if not filepath:
  255             continue
  256         filepath = os.path.expanduser(filepath)
  257         if not os.path.isabs(filepath):
  258             filepath = os.path.join(homedir, filepath)
  259         config.set(category, filename, filepath)