Merge branch '12721-build-package-version'
[arvados.git] / sdk / pam / arvados_pam / auth_event.py
1 # Copyright (C) The Arvados Authors. All rights reserved.
2 #
3 # SPDX-License-Identifier: Apache-2.0
4
5 import arvados
6 import syslog
7
8 def auth_log(msg):
9     """Log an authentication result to syslogd"""
10     syslog.openlog(facility=syslog.LOG_AUTH)
11     syslog.syslog('arvados_pam: ' + msg)
12     syslog.closelog()
13
14 class AuthEvent(object):
15     def __init__(self, config, service, client_host, username, token):
16         self.config = config
17         self.service = service
18         self.client_host = client_host
19         self.username = username
20         self.token = token
21
22         self.api_host = None
23         self.vm_uuid = None
24         self.user = None
25
26     def can_login(self):
27         """Return truthy IFF credentials should be accepted."""
28         ok = False
29         try:
30             self.api_host = self.config['arvados_api_host']
31             self.arv = arvados.api('v1', host=self.api_host, token=self.token,
32                                    insecure=self.config.get('insecure', False),
33                                    cache=False)
34
35             vmname = self.config['virtual_machine_hostname']
36             vms = self.arv.virtual_machines().list(filters=[['hostname','=',vmname]]).execute()
37             if vms['items_available'] > 1:
38                 raise Exception("lookup hostname %s returned %d records" % (vmname, vms['items_available']))
39             if vms['items_available'] == 0:
40                 raise Exception("lookup hostname %s not found" % vmname)
41             vm = vms['items'][0]
42             if vm['hostname'] != vmname:
43                 raise Exception("lookup hostname %s returned hostname %s" % (vmname, vm['hostname']))
44             self.vm_uuid = vm['uuid']
45
46             self.user = self.arv.users().current().execute()
47
48             filters = [
49                 ['link_class','=','permission'],
50                 ['name','=','can_login'],
51                 ['head_uuid','=',self.vm_uuid],
52                 ['tail_uuid','=',self.user['uuid']]]
53             for l in self.arv.links().list(filters=filters, limit=10000).execute()['items']:
54                 if (l['properties']['username'] == self.username and
55                     l['tail_uuid'] == self.user['uuid'] and
56                     l['head_uuid'] == self.vm_uuid and
57                     l['link_class'] == 'permission' and
58                     l['name'] == 'can_login'):
59                     return self._report(True)
60
61             return self._report(False)
62
63         except Exception as e:
64             return self._report(e)
65
66     def _report(self, result):
67         """Log the result. Return truthy IFF result is True.
68
69         result must be True, False, or an exception.
70         """
71         self.result = result
72         auth_log(self.message())
73         return result == True
74
75     def message(self):
76         """Return a log message describing the event and its outcome."""
77         if isinstance(self.result, Exception):
78             outcome = 'Error: ' + repr(self.result)
79         elif self.result == True:
80             outcome = 'Allow'
81         else:
82             outcome = 'Deny'
83
84         if len(self.token) > 40:
85             log_token = self.token[0:15]
86         else:
87             log_token = '<invalid>'
88
89         log_label = [self.service, self.api_host, self.vm_uuid, self.client_host, self.username, log_token]
90         if self.user:
91             log_label += [self.user.get('uuid'), self.user.get('full_name')]
92         return str(log_label) + ': ' + outcome