Misc auth fixes
[uccvend-vendserver.git] / VendServer / OpenDispense.py
1 """
2 Author: Mitchell Pomery (bobgeorge33)
3
4 System to connect to OpenDispense2.
5 Most of this code has been copied out of VendServer.py, then had variables updated so that it runs.
6 This is so VendServer can easily operate regardless of the current accounting backend.
7 Documentation for this code can be found inder Dispence.DispenceInterface
8 """
9
10 from DispenseInterface import DispenseInterface
11 import os
12 import logging
13 import re
14 import pwd
15 import base64
16 import socket
17 from subprocess import Popen, PIPE
18 from LDAPConnector import get_uid,get_uname, set_card_id
19
20 DISPENSE_ENDPOINT = ("localhost", 11020)
21 DISPSRV_MIFARE = True
22
23 class OpenDispense(DispenseInterface):
24         _username = ""
25         _disabled = True
26         _loggedIn = False
27         _userId = None
28
29         def __init__(self, username=None, secret=False):
30                 pass
31
32         def authUserIdPin(self, userId, pin):
33                 userId = int(userId)
34
35                 try:
36                         # Get info from 
37                         info = pwd.getpwuid(userId)
38                 except KeyError:
39                         logging.info('getting pin for uid %d: user not in password file'%userId)
40                         return False
41
42                 if info.pw_dir == None: return False
43                 pinfile = os.path.join(info.pw_dir, '.pin')
44                 try:
45                         s = os.stat(pinfile)
46                 except OSError:
47                         logging.info('getting pin for uid %d: .pin not found in home directory'%userId)
48                         return False
49                 if s.st_mode & 077:
50                         logging.info('getting pin for uid %d: .pin has wrong permissions. Fixing.'%userId)
51                         os.chmod(pinfile, 0600)
52                 try:
53                         f = file(pinfile)
54                 except IOError:
55                         logging.info('getting pin for uid %d: I cannot read pin file'%userId)
56                         return False
57                 pinstr = f.readline().strip()
58                 f.close()
59                 if not re.search('^[0-9]{4}$', pinstr):
60                         logging.info('getting pin for uid %d: %s not a good pin'%(userId,repr(pinstr)))
61                         return False
62
63                 if pinstr == str(pin):
64                         #Login Successful
65                         self._userid = userId
66                         self._loggedIn = True
67                         self._disabled = False
68                         self._username = info.pw_name
69                         return True
70                 
71                 # Login Unsuccessful
72                 return False
73
74         def authMifareCard(self, cardId):
75                 self._loggedIn = False
76                 self._username = None
77                 if DISPSRV_MIFARE:
78                         card_base64 = base64.b64encode(cardId)
79                         
80                         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
81                         sock.connect(DISPENSE_ENDPOINT)
82                         logging.debug('connected to dispsrv')
83                         sockf = sock.makefile()
84                         sockf.write("AUTHIDENT\n"); sockf.flush()
85                         rsp = sockf.readline()
86                         assert "200" in rsp
87                         logging.debug('authenticated')
88                         sockf.write("AUTHCARD %s\n" % (card_base64,)); sockf.flush()
89                         rsp = sockf.readline()
90                         if not "200" in rsp:
91                                 return False
92                         username = rsp.split('=')[1].strip()
93
94                         # Check for thier username
95                         try:
96                                 # Get info from the system (by username)
97                                 info = pwd.getpwnam(username)
98                         except KeyError:
99                                 logging.info('getting info for user \'%s\': user not in password file' % (username,))
100                                 return False
101                 else:
102                         # Get the users ID
103                         self._userid = get_uid(cardId)
104
105                         # Check for thier username
106                         try:
107                                 # Get info from the system (by UID)
108                                 info = pwd.getpwuid(self._userid)
109                         except KeyError:
110                                 logging.info('getting info for uid %d: user not in password file' % (self._userid,))
111                                 return False
112
113                 # If we get this far all is good
114                 self._loggedIn = True
115                 self._disabled = False
116                 self._userid = info.pw_uid
117                 self._username = info.pw_name
118                 return True
119         def logOut(self):
120             self._loggedIn = False
121             self._disabled = False
122             self._userId = None
123             self._username = None
124
125         def addCard(self, cardId):
126                 if not self.isLoggedIn():
127                         return False
128                 if DISPSRV_MIFARE:
129                         card_base64 = base64.b64encode(cardId)
130                         logging.info('Enrolling card %s to uid %s (%s)' % (cardId, self._userId, self._username))
131                         sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
132                         sock.connect(DISPENSE_ENDPOINT)
133                         sockf = sock.makefile()
134                         sockf.write("AUTHIDENT\n")
135                         sockf.flush(); rsp = sockf.readline()
136                         assert "200" in rsp
137                         sockf.write("SETEUSER %s\n" % (self._username,))
138                         sockf.flush(); rsp = sockf.readline()
139                         assert "200" in rsp
140                         sockf.write("CARD_ADD %s\n" % (card_base64,))
141                         sockf.flush(); rsp = sockf.readline()
142                         if "200" in rsp:
143                                 return True
144                         else:
145                                 return False
146                 else:
147                         if get_uid(cardId) != None:
148                                 return False
149                         else:
150                                 logging.info('Enrolling card %s to uid %s (%s)' % (cardId, self._userId, self._username))
151                                 set_card_id(self._userId, cardId)
152                                 return True
153
154         def isLoggedIn(self):
155                 return self._loggedIn
156
157         def getUsername(self):
158                 return self._username
159
160         def getBalance(self):
161                 # Balance checking
162                 if self.isLoggedIn():
163                         acct, unused = Popen(['dispense', 'acct', self._username], close_fds=True, stdout=PIPE).communicate()
164                 else:
165                         return None
166                 balance = acct[acct.find("$")+1:acct.find("(")].strip()
167                 return balance
168
169         def getItemInfo(self, itemId):
170                 logging.debug("getItemInfo(%s)" % (itemId,))
171                 itemId = OpenDispenseMapping.vendingMachineToOpenDispense(itemId)
172                 args = ('dispense', 'iteminfo', itemId)
173                 info, unused = Popen(args, close_fds=True, stdout=PIPE).communicate()
174                 m = re.match("\s*[a-z]+:\d+\s+(\d+)\.(\d\d)\s+([^\n]+)", info)
175                 if m == None:
176                         return("dead", 0)
177                 cents = int(m.group(1))*100 + int(m.group(2))
178                 # return (name, price in cents)
179                 return (m.group(3), cents)
180
181         def isDisabled(self):
182                 return self._disabled
183
184         def dispenseItem(self, itemId):
185                 if not self.isLoggedIn() or self.getItemInfo(itemId)[0] == "dead":
186                         return False
187                 else:
188                         print('dispense -u "%s" %s'%(self._username, itemId))
189                         #os.system('dispense -u "%s" %s'%(self._username, itemId))
190                         return True
191
192         def logOut(self):
193                 self._username = ""
194                 self._disabled = True
195                 self._loggedIn = False
196                 self._userId = None
197
198 """
199 This class abstracts the idea of item numbers.
200 It removes the need for VendServer to know the mappings between inputted numbers
201 and the equivalent itemId in OpenDispense.
202 """
203 class OpenDispenseMapping():
204
205         @staticmethod
206         def vendingMachineToOpenDispense(itemId):
207                 logging.debug("vendingMachineToOpenDispense(%s)" % (itemId,))
208                 _mappingFile = "OpenDispenseMappings.conf"
209                 try:
210                         fh = open(_mappingFile)
211                 except IOError:
212                         if itemId[1] == '8':
213                                 return 'coke:' + itemId[0]
214                         elif itemId[1] == '5':
215                                 if itemId[0] == '5':
216                                         return 'door'
217                                 else:
218                                         return None
219                         else:
220                                 return 'snack:' + itemId
221                 map = ""
222                 for line in fh:
223                         #line = line.strip()
224                         if line.startswith(str(itemId) + " "):
225                                 map = line[len(str(itemId)) + 1:].strip()
226                                 print(map)
227                 return map
228

UCC git Repository :: git.ucc.asn.au