You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

186 lines
6.8 KiB

import base64
import json
import requests
import struct
import time
from django.contrib.auth.models import User
from rest_framework import mixins, permissions, status, viewsets
from rest_framework.authtoken.models import Token
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from . import models, serializers
from authserver.settings import PROTOSPACE_LOGIN_PAGE
LOG_DIRECTORY = '/var/log/pslockout'
VALID_TIME = 1000000000
class IsLockoutAdmin(permissions.BasePermission):
def has_permission(self, request, view):
try:
return request.user.profile.lockout_admin
except AttributeError:
return False
class CategoryViewSet(viewsets.ModelViewSet):
queryset = models.Category.objects.all().order_by('id')
serializer_class = serializers.CategorySerializer
permission_classes = (IsLockoutAdmin,)
lookup_field='slug'
class CourseViewSet(viewsets.ModelViewSet):
queryset = models.Course.objects.all().order_by('id')
serializer_class = serializers.CourseSerializer
permission_classes = (IsLockoutAdmin,)
lookup_field='slug'
class ToolViewSet(viewsets.ModelViewSet):
queryset = models.Tool.objects.all().order_by('id')
serializer_class = serializers.ToolSerializer
permission_classes = (IsLockoutAdmin,)
lookup_field='slug'
class ToolDataViewSet(viewsets.ViewSet):
def list(self, request):
objects = models.Category.objects.all().order_by('id')
serializer = serializers.ToolDataSerializer(objects, many=True, context={'request': request})
return Response({'categories': serializer.data})
class ProfileViewSet(
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet):
queryset = models.Profile.objects.all().order_by('-user__date_joined')
serializer_class = serializers.ProfileSerializer
permission_classes = (IsLockoutAdmin,)
class UserViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = serializers.UserSerializer
permission_classes = (permissions.IsAuthenticated,)
def get_queryset(self):
return User.objects.filter(username=self.request.user)
@api_view(["POST"])
def login(request):
username = request.data.get("username").lower()
password = request.data.get("password")
if username is None or password is None:
return Response({'error': 'Please provide both username and password'},
status=status.HTTP_400_BAD_REQUEST)
post_data = {'user_name': username, 'web_pw': password, 'SubmitButton': 'Login'}
res = requests.post(PROTOSPACE_LOGIN_PAGE, post_data, allow_redirects=False)
if res.status_code == requests.codes.ok:
return Response({'error': 'Invalid Credentials'}, status=status.HTTP_404_NOT_FOUND)
lockout_username = username.replace('.', '')
user, created = User.objects.get_or_create(username=lockout_username)
user.set_password(password) # not validated
user.save()
if created:
models.Profile.objects.create(user=user)
token, _ = Token.objects.get_or_create(user=user)
return Response({'token': token.key}, status=status.HTTP_200_OK)
@api_view(["GET"])
def cards(request, mac):
cards = models.Card.objects.all().filter(profile__courses__tools__mac=mac)
card_numbers = [card.number for card in cards]
return Response(','.join(card_numbers), status=status.HTTP_200_OK)
@api_view(["PUT"])
@permission_classes((IsLockoutAdmin,))
def update_cards(request):
data = request.data
updated_count = 0
if not data:
return Response({'error': 'Please provide card data in the form username=cardnumber,cardnumber,cardnumber'},
status=status.HTTP_400_BAD_REQUEST)
for username, card_numbers in data.items():
try:
lockout_username = username.replace('.', '')
profile = models.Profile.objects.get(user__username=lockout_username)
except models.Profile.DoesNotExist:
continue
for card_number in card_numbers.split(','):
card, _ = models.Card.objects.get_or_create(
profile=profile,
number=card_number
)
if card: updated_count += 1
return Response({'updated': updated_count}, status=status.HTTP_200_OK)
EVENTS = [
'LOG_BOOT_UP - Booted up =============================================',
'LOG_INIT_COMPLETE - Initialization completed',
'LOG_WIFI_CONNECTED - Wifi connected',
'LOG_WIFI_DISCONNECTED - Wifi disconnected',
'LOG_COMM_LOCK_ARM - Received arm request over web',
'LOG_COMM_LOCK_DISARM - Received disarm request over web',
'LOG_COMM_LOCK_FAIL - Lock status communication failed, code: ',
'LOG_COMM_CARD_FAIL - Card list communication failed, code: ',
'LOG_COMM_INFO_FAIL - Info log communication failed, code: ',
'LOG_LOCK_OFF - Lock turned off',
'LOG_LOCK_ARMED - Lock armed',
'LOG_LOCK_TIMEOUT - Lock arming timed out',
'LOG_LOCK_ON - Lock turned on',
'LOG_LOCK_DISARMED - Lock disarmed',
'LOG_LOCK_ERROR - Button held while arming lock',
'LOG_CARD_GOOD_READ - Successful read from card: ',
'LOG_CARD_ACCEPTED - Accepted card: ',
'LOG_CARD_DENIED - Denied card: ',
]
@api_view(["POST"])
def infolog(request, mac):
entries_processed = 0
oldest_valid_log_time = time.time()
encoded_log = request.data.get('log')
if encoded_log:
decoded_log = base64.b64decode(encoded_log)
unpacked_log = list(struct.iter_unpack('<IB10s', decoded_log))
for entry in unpacked_log:
if entry[0] < oldest_valid_log_time and entry[0] > VALID_TIME:
oldest_valid_log_time = entry[0]
with open(LOG_DIRECTORY + '/devices/' + mac + '.log', 'a') as log_file:
for entry in unpacked_log:
# if time is obviously wrong, just use oldest valid log time or now
entry_time = entry[0] if entry[0] > VALID_TIME else oldest_valid_log_time
entry_time_string = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(entry_time))
entry_event = EVENTS[entry[1]]
entry_data = entry[2].decode('utf-8').strip('\0')
if entry_data:
user = models.User.objects.filter(profile__cards__number=entry_data)
if user.count():
entry_data += ' (' + str(user.first()) + ')'
entry_string = '{} - {} - {}{}'.format(entry_time_string, mac, entry_event, entry_data)
entries_processed += 1
log_file.write(entry_string + '\n')
response_object = {
'unixTime': int(time.time()),
'processed': entries_processed,
}
return Response(response_object, status=status.HTTP_200_OK)