[feature] reactor-based authenticator
[leap_pycommon.git] / src / leap / common / events / auth.py
1 # -*- coding: utf-8 -*-
2 # auth.py
3 # Copyright (C) 2016 LEAP
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 """
18 ZAP authentication, twisted style.
19 """
20 from zmq import PAIR
21 from zmq.auth.base import Authenticator, VERSION
22 from txzmq.connection import ZmqConnection
23 from zmq.utils.strtypes import b, u
24
25 from twisted.python import log
26
27 from txzmq.connection import ZmqEndpoint, ZmqEndpointType
28
29
30 class TxAuthenticator(ZmqConnection):
31
32     """
33     This does not implement the whole ZAP protocol, but the bare minimum that
34     we need.
35     """
36
37     socketType = PAIR
38     address = 'inproc://zeromq.zap.01'
39     encoding = 'utf-8'
40
41     def __init__(self, factory):
42         super(TxAuthenticator, self).__init__(factory)
43         self.authenticator = Authenticator(factory.context)
44         self.authenticator._send_zap_reply = self._send_zap_reply
45
46     def start(self):
47         endpoint = ZmqEndpoint(ZmqEndpointType.bind, self.address)
48         self.addEndpoints([endpoint])
49
50     def messageReceived(self, msg):
51
52         command = msg[0]
53
54         if command == b'ALLOW':
55             addresses = [u(m, self.encoding) for m in msg[1:]]
56             try:
57                 self.authenticator.allow(*addresses)
58             except Exception as e:
59                 log.err("Failed to allow %s", addresses)
60
61         elif command == b'CURVE':
62             domain = u(msg[1], self.encoding)
63             location = u(msg[2], self.encoding)
64             self.authenticator.configure_curve(domain, location)
65
66     def _send_zap_reply(self, request_id, status_code, status_text,
67                         user_id='user'):
68         """
69         Send a ZAP reply to finish the authentication.
70         """
71         user_id = user_id if status_code == b'200' else b''
72         if isinstance(user_id, unicode):
73             user_id = user_id.encode(self.encoding, 'replace')
74         metadata = b''  # not currently used
75         reply = [VERSION, request_id, status_code, status_text,
76                  user_id, metadata]
77         self.send(reply)
78
79
80 class TxAuthenticationRequest(ZmqConnection):
81
82     socketType = PAIR
83     address = 'inproc://zeromq.zap.01'
84     encoding = 'utf-8'
85
86     def start(self):
87         endpoint = ZmqEndpoint(ZmqEndpointType.connect, self.address)
88         self.addEndpoints([endpoint])
89
90     def allow(self, *addresses):
91         self.send([b'ALLOW'] + [b(a, self.encoding) for a in addresses])
92
93     def configure_curve(self, domain='*', location=''):
94         domain = b(domain, self.encoding)
95         location = b(location, self.encoding)
96         self.send([b'CURVE', domain, location])