"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)