]>
Commit | Line | Data |
---|---|---|
1 | #!/usr/bin/python | |
2 | ||
3 | import configparser | |
4 | import io | |
5 | import location | |
6 | import logging | |
7 | import os | |
8 | import ssl | |
9 | import tempfile | |
10 | import tornado.httpclient | |
11 | ||
12 | from . import accounts | |
13 | from . import asterisk | |
14 | from . import analytics | |
15 | from . import blog | |
16 | from . import bugzilla | |
17 | from . import cache | |
18 | from . import campaigns | |
19 | from . import database | |
20 | from . import fireinfo | |
21 | from . import httpclient | |
22 | from . import iuse | |
23 | from . import messages | |
24 | from . import mirrors | |
25 | from . import netboot | |
26 | from . import nopaste | |
27 | from . import ratelimit | |
28 | from . import releases | |
29 | from . import resolver | |
30 | from . import settings | |
31 | from . import toots | |
32 | from . import util | |
33 | from . import wiki | |
34 | from . import zeiterfassung | |
35 | from .decorators import * | |
36 | ||
37 | DEFAULT_CONFIG = io.StringIO(""" | |
38 | [global] | |
39 | debug = false | |
40 | environment = testing | |
41 | ||
42 | data_dir = | |
43 | static_dir = %(data_dir)s/static | |
44 | templates_dir = %(data_dir)s/templates | |
45 | """) | |
46 | ||
47 | # Setup logging | |
48 | log = logging.getLogger(__name__) | |
49 | ||
50 | class Backend(object): | |
51 | version = 0 | |
52 | ||
53 | def __init__(self, configfile, debug=False): | |
54 | # Read configuration file. | |
55 | self.config = self.read_config(configfile) | |
56 | ||
57 | # Enable debug logging if configured | |
58 | self.debug = debug or self.config.getboolean("global", "debug") | |
59 | ||
60 | # Setup database. | |
61 | self.setup_database() | |
62 | ||
63 | # Create HTTPClient | |
64 | self.http_client = httpclient.HTTPClient(self) | |
65 | ||
66 | # Initialize the cache | |
67 | self.cache = cache.Cache(self) | |
68 | ||
69 | # Initialize settings first | |
70 | self.settings = settings.Settings(self) | |
71 | ||
72 | # Initialize backend modules. | |
73 | self.accounts = accounts.Accounts(self) | |
74 | self.analytics = analytics.Analytics(self) | |
75 | self.bugzilla = bugzilla.Bugzilla(self) | |
76 | self.fireinfo = fireinfo.Fireinfo(self) | |
77 | self.iuse = iuse.IUse(self) | |
78 | self.mirrors = mirrors.Mirrors(self) | |
79 | self.netboot = netboot.NetBoot(self) | |
80 | self.nopaste = nopaste.Nopaste(self) | |
81 | self.releases = releases.Releases(self) | |
82 | ||
83 | self.blog = blog.Blog(self) | |
84 | self.wiki = wiki.Wiki(self) | |
85 | self.zeiterfassung = zeiterfassung.ZeiterfassungClient(self) | |
86 | ||
87 | def read_config(self, configfile): | |
88 | cp = configparser.ConfigParser() | |
89 | ||
90 | # Initialize configuration with some sensible defaults | |
91 | cp.readfp(DEFAULT_CONFIG) | |
92 | ||
93 | # Parse file | |
94 | cp.read(configfile) | |
95 | ||
96 | return cp | |
97 | ||
98 | @property | |
99 | def environment(self): | |
100 | """ | |
101 | Returns whether this is running in "production" or "testing" | |
102 | """ | |
103 | # Fetch from the environment | |
104 | try: | |
105 | return os.environ["ENVIRONMENT"] | |
106 | except KeyError: | |
107 | pass | |
108 | ||
109 | # Fall back to the configuration | |
110 | return self.config.get("global", "environment") | |
111 | ||
112 | def setup_database(self): | |
113 | """ | |
114 | Sets up the database connection. | |
115 | """ | |
116 | credentials = { | |
117 | "host" : self.config.get("database", "server"), | |
118 | "database" : self.config.get("database", "database"), | |
119 | "user" : self.config.get("database", "username"), | |
120 | "password" : self.config.get("database", "password"), | |
121 | } | |
122 | ||
123 | self.db = database.Connection(self, **credentials) | |
124 | ||
125 | @lazy_property | |
126 | def ssl_context(self): | |
127 | # Create SSL context | |
128 | context = ssl.create_default_context() | |
129 | ||
130 | # Fetch client certificate | |
131 | certificate = self.settings.get("client-certificate", None) | |
132 | key = self.settings.get("client-key", None) | |
133 | ||
134 | # Apply client certificate | |
135 | if certificate and key: | |
136 | with tempfile.NamedTemporaryFile(mode="w") as f_cert: | |
137 | f_cert.write(certificate) | |
138 | f_cert.flush() | |
139 | ||
140 | with tempfile.NamedTemporaryFile(mode="w") as f_key: | |
141 | f_key.write(key) | |
142 | f_key.flush() | |
143 | ||
144 | context.load_cert_chain(f_cert.name, f_key.name) | |
145 | ||
146 | return context | |
147 | ||
148 | async def load_certificate(self, certfile, keyfile): | |
149 | with self.db.transaction(): | |
150 | # Load certificate | |
151 | with open(certfile) as f: | |
152 | self.settings.set("client-certificate", f.read()) | |
153 | ||
154 | # Load key file | |
155 | with open(keyfile) as f: | |
156 | self.settings.set("client-key", f.read()) | |
157 | ||
158 | async def run_task(self, task, *args, **kwargs): | |
159 | tasks = { | |
160 | "accounts:delete" : self.accounts._delete, | |
161 | "announce-blog-posts" : self.blog.announce, | |
162 | "campaigns:donate" : self.campaigns.donate, | |
163 | "campaigns:send" : self.campaigns.send, | |
164 | "check-mirrors" : self.mirrors.check_all, | |
165 | "cleanup" : self.cleanup, | |
166 | "get-all-emails" : self.accounts.get_all_emails, | |
167 | "load-certificate" : self.load_certificate, | |
168 | "scan-files" : self.releases.scan_files, | |
169 | "send-message" : self.messages.send_cli, | |
170 | "send-all-messages" : self.messages.queue.send_all, | |
171 | "test-ldap" : self.accounts.test_ldap, | |
172 | "toot" : self.toots.toot, | |
173 | "update-blog-feeds" : self.blog.update_feeds, | |
174 | } | |
175 | ||
176 | # Get the task from the list of all tasks | |
177 | func = tasks.get(task, None) | |
178 | if not func: | |
179 | raise ValueError("Unknown task: %s" % task) | |
180 | ||
181 | # Check if we are running in production | |
182 | if not self.environment == "production": | |
183 | log.warning("Refusing to run task '%s' in '%s' environment" % (task, self.environment)) | |
184 | return | |
185 | ||
186 | # Run the task | |
187 | with self.db.transaction(): | |
188 | r = await func(*args, **kwargs) | |
189 | ||
190 | # If any error code has been returned, | |
191 | # we will end the program | |
192 | if r: | |
193 | raise SystemExit(r) | |
194 | ||
195 | @lazy_property | |
196 | def asterisk(self): | |
197 | return asterisk.Asterisk(self) | |
198 | ||
199 | @lazy_property | |
200 | def campaigns(self): | |
201 | return campaigns.Campaigns(self) | |
202 | ||
203 | @lazy_property | |
204 | def groups(self): | |
205 | return accounts.Groups(self) | |
206 | ||
207 | @lazy_property | |
208 | def messages(self): | |
209 | return messages.Messages(self) | |
210 | ||
211 | @lazy_property | |
212 | def location(self): | |
213 | return location.Database("/var/lib/location/database.db") | |
214 | ||
215 | def get_country_name(self, country_code): | |
216 | try: | |
217 | country = self.location.get_country(country_code) | |
218 | ||
219 | # In case the country code was invalid, we return it again | |
220 | except ValueError: | |
221 | return country_code | |
222 | ||
223 | if country: | |
224 | return country.name | |
225 | ||
226 | return country_code | |
227 | ||
228 | @lazy_property | |
229 | def ratelimiter(self): | |
230 | return ratelimit.RateLimiter(self) | |
231 | ||
232 | @lazy_property | |
233 | def resolver(self): | |
234 | return resolver.Resolver(tries=2, timeout=2, domains=[]) | |
235 | ||
236 | @lazy_property | |
237 | def toots(self): | |
238 | return toots.Toots(self) | |
239 | ||
240 | async def cleanup(self): | |
241 | # Cleanup message queue | |
242 | with self.db.transaction(): | |
243 | self.messages.queue.cleanup() | |
244 | ||
245 | # Cleanup in accounts | |
246 | with self.db.transaction(): | |
247 | self.accounts.cleanup() | |
248 | ||
249 | # Cleanup nopasts | |
250 | with self.db.transaction(): | |
251 | self.nopaste.cleanup() |