diff --git a/README.md b/README.md index 84c15326a8ab3bdb54469324adc7d6c50ecf273b..ca67611b312ca29a3502bf392279e66e00255fd2 100644 --- a/README.md +++ b/README.md @@ -1,38 +1,4 @@ # Projektbeschreibung -WarpInfra ist eine zentrale Applikationsplattform für die Verwaltung von Benutzer konten und zur Bereitstellung weiterer Dienste. -WarpInfra ist modular aufgebaut um einfach weitere Anwendungen aufnehmen zu können. - - * Die Produktive Anwendung ist jeweils unter https://infra.warpzone.ms erreichbar - * Das Staging System ist unter https://infra-test.warpzone.ms erreichbar. - -## WarpAuth - -Das Modul WarpAuth übernimmt die Verwaltung von Benutzerkonnten und unterstützt bei der Vereindung sowie Einrichtung der 2-Factor Authentifizierung. -Die Benutzerkonten werden in einem LDAP Verzeichnis gespeichert und können von weiteren Applikationen verwendet werden. - -Aktuell sind der GitLab und der Mattermost Server angebunden. - -Dieses Modul ist Stabil und kann produktiv verwendet werden - -## WarpFood - -WarpFood implementiert eine Abwendung zur erstellung von Pizza- bzw. Essensbestellungen. -Neue Bestellungen werden als Info in den Mattermost Channel Town-Squre publiziert. - -Dieses Modul ist Stabil und kann produktiv verwendet werden - - -## WarpAPI - -Arbeitsbasis zur Re-Implemmentierung der Hackerspace-API. - -Dieses Modul ist noch in Entwicklung und auf der Produktivistanz nicht aktiviert. - - -## WarpPay - -Prototyp zur Entwicklung eines neuen Kasseen bzw. PrePay System. - -Dieses Modul ist noch in Entwicklung und auf keiner der Instanzen nicht aktiviert. +s. Wiki: https://wiki.warpzone.ms/infrastruktur:warpinfra diff --git a/www/Dockerfile b/www/Dockerfile index 24d6db8d712059129ef5f34cff5c09f0cf673259..dea0c4ce014b59a6166b43cd12152ef3c43f6e24 100644 --- a/www/Dockerfile +++ b/www/Dockerfile @@ -37,6 +37,7 @@ RUN pip3 install \ markdown \ django-mysql \ mysqlclient \ + bcrypt \ --upgrade RUN pip3 install git+https://github.com/nkunihiko/django-bootstrap3-datetimepicker.git diff --git a/www/web/templates/base.html b/www/web/templates/base.html index 00cbb5fc0793581762ccae5228c041191e6392e8..55ec82e5ea71185febf726c5b5ba37c76f8a57c6 100644 --- a/www/web/templates/base.html +++ b/www/web/templates/base.html @@ -71,7 +71,7 @@ {% else %} <ul class="nav navbar-nav"> - <li><a href="/pizza">{% trans "pizza_sheet" %}</a></li> + <li><a href="https://infra.warpzone.ms/pizza">{% trans "pizza_sheet" %}</a></li> </ul> {% endif %} <ul class="nav navbar-nav"> diff --git a/www/web/warpauth/models.py b/www/web/warpauth/models.py index 2c7bd34339e6b6290b5643f66f33fa529bc26206..e84a6ab88a1dc590d53b13d23d5a5425cbc59d46 100644 --- a/www/web/warpauth/models.py +++ b/www/web/warpauth/models.py @@ -38,6 +38,7 @@ class LdapUser(ldapdb.models.Model): email = CharField(db_column='mail', max_length=200) cn = CharField(db_column='cn', max_length=200) card_id = CharField(db_column='employeeNumber', max_length=200) + pinCode = CharField(db_column='carLicense', max_length=100) def __str__(self): return self.uid diff --git a/www/web/warpauth/templates/warpauth/profile.html b/www/web/warpauth/templates/warpauth/profile.html index a1b4b66fcfc993c6500f03ad1dc179b9ee660eb0..f8930e472945ce819f1c7fb36dfd38fd7ccca694 100644 --- a/www/web/warpauth/templates/warpauth/profile.html +++ b/www/web/warpauth/templates/warpauth/profile.html @@ -15,6 +15,9 @@ <li role="presentation"> <a href="#change_passwd" aria-controls="change_passwd" role="tab" data-toggle="tab">{% trans "change_password" %}</a> </li> + <li role="presentation"> + <a href="#warp_pay" aria-controls="warp_pay" role="tab" data-toggle="tab">{% trans "warp_pay" %}</a> + </li> <li role="presentation"> <a href="/account/two_factor/">{% trans "two_factor_authentication" %}</a> </li> @@ -27,6 +30,9 @@ <div role="tabpanel" class="tab-pane fade in" id="change_passwd"> {% include "warpauth/profile/change_password.html" %} </div> + <div role="tabpanel" class="tab-pane fade in" id="warp_pay"> + {% include "warpauth/profile/warp_pay.html" %} + </div> </div> </div> <script> diff --git a/www/web/warpauth/templates/warpauth/profile/warp_pay.html b/www/web/warpauth/templates/warpauth/profile/warp_pay.html new file mode 100644 index 0000000000000000000000000000000000000000..c74cb2ace5deeebad149721989aa6f455f6725f9 --- /dev/null +++ b/www/web/warpauth/templates/warpauth/profile/warp_pay.html @@ -0,0 +1,40 @@ +{% load i18n %} + +<div> + <br /> + {% if error_warp_pay_settings %} + <div class="alert alert-danger">{{ error_warp_pay_settings }}</div><br> + {% endif %} + {% if success_warp_pay_settings %} + <div class="alert alert-success">{% trans "settings_changed_successful" %}</div><br> + {% endif %} + + <form class="form-horizontal" method="POST" action="/account/profile/warp_pay_settings/" role="form"> + {% csrf_token %} + <!--<div class="form-group"> + <label class="control-label col-sm-2 col-lg-2 " for="id_curr_pw">{% trans "current_password" %}</label> + <div class=" col-sm-10 col-lg-10 "> + <input class=" form-control" id="id_curr_pw" name="curr_pw" type="password" /> + </div> + </div>--> + <div class="form-group"> + <label class="control-label col-sm-2 col-lg-2 " for="id_new_pin">{% trans "new_pin_code" %}</label> + <div class=" col-sm-10 col-lg-10 "> + <input class=" form-control" id="id_new_pin" name="new_pin" type="password" /> + </div> + </div> + <div class="form-group"> + <label class="control-label col-sm-2 col-lg-2 " for="id_new_pin_confirm">{% trans "confirm_pin_code" %}</label> + <div class=" col-sm-10 col-lg-10 "> + <input class=" form-control" id="id_new_pin_confirm" name="new_pin_confirm" type="password" /> + </div> + </div> + <div class="form-group"> + <div class="col-sm-offset-2 col-sm-10"> + <button style="float: left" type="submit" formmethod="post" class="btn btn-primary">{% trans "save_settings" %}</button> + </div> + </div> + </form> +</div> + + diff --git a/www/web/warpauth/urls.py b/www/web/warpauth/urls.py index cfe293e08edad6e991102e58ae9b36527f5be9ef..d916b767a339e2ecca80603e6f21aa9e1e2a23ed 100644 --- a/www/web/warpauth/urls.py +++ b/www/web/warpauth/urls.py @@ -14,6 +14,7 @@ urlpatterns = [ url(r'^account/profile/$', profile.index, name='profile_index'), url(r'^account/profile/change_password/$', profile.change_password, name='change_password'), url(r'^account/profile/change_information/$', profile.change_information, name='change_information'), + url(r'^account/profile/warp_pay_settings/$', profile.warp_pay_settings, name='warp_pay_settings'), ] diff --git a/www/web/warpauth/views/profile.py b/www/web/warpauth/views/profile.py index 3505b7e1904b6da83a6812497548a9d70503cdae..93a656964fa4abf2de4fb10d17e549c9c8e41174 100644 --- a/www/web/warpauth/views/profile.py +++ b/www/web/warpauth/views/profile.py @@ -9,6 +9,7 @@ from warpauth.models import LdapUser, LdapUserForm from warpauth.util import * import logging from django.conf import settings +import bcrypt ## # http://www.python-ldap.org/doc/html/ldap.html#ldap.LDAPObject @@ -26,6 +27,9 @@ def clear_error_messages(): page_context["success_info"] = False page_context["error_passwd"] = "" page_context["success_passwd"] = False + page_context["error_warp_pay_settings"] = "" + page_context["success_warp_pay_settings"] = False + page_context["selected_tab"] = "" @@ -44,7 +48,6 @@ def change_information(request): if request.method != 'POST': return redirect("profile_index") - user = LdapUser.objects.get(uid=str(request.user)) if "first_name" in request.POST: first_name = request.POST["first_name"] @@ -55,7 +58,7 @@ def change_information(request): card_id = request.POST["card_id"] else: card_id = 0 - + if "email" not in request.POST: page_context["error_info"] = "err_invalid_email" return HttpResponse(render(request, 'warpauth/profile.html', page_context)) @@ -80,7 +83,8 @@ def change_information(request): user.last_name = last_name user.cn = cn user.email = email - user.card_id=card_id + user.card_id = card_id + user.save() page_context["success_info"] = True @@ -88,6 +92,26 @@ def change_information(request): return HttpResponse(render(request, 'warpauth/profile.html', page_context)) +@login_required(login_url=settings.LOGIN_URL, redirect_field_name=None) +def warp_pay_settings(request): + page_context["selected_tab"] = "#warp_pay" + if request.method == 'POST': + user = LdapUser.objects.get(uid=str(request.user)) + if request.POST["new_pin"] != "": + # if not "curr_pw" in request.POST or not request.user.check_password(request.POST["curr_pw"]): + # page_context["error_warp_pay_settings"] = "wrong_password" + if request.POST["new_pin"].isnumeric() and request.POST["new_pin"] == request.POST["new_pin_confirm"]: + pin = request.POST["new_pin"].encode('utf-8') + user.pinCode = bcrypt.hashpw(pin, bcrypt.gensalt(10,prefix=b"2a")).decode('utf-8') + user.save() + page_context["success_warp_pay_settings"] = True + else: + page_context["error_warp_pay_settings"] = "pin_repeat_wrong" + + set_user_object(request) + return HttpResponse(render(request, 'warpauth/profile.html', page_context)) + + @login_required(login_url=settings.LOGIN_URL, redirect_field_name=None) def change_password(request): clear_error_messages() diff --git a/www/web/warpauth/views/register.py b/www/web/warpauth/views/register.py index 099f678fe0d68d6d60957a9bf556c4e608cd468d..5721f8712daa8cf2cf9513bd6e36718d166bdb2a 100644 --- a/www/web/warpauth/views/register.py +++ b/www/web/warpauth/views/register.py @@ -10,7 +10,8 @@ from warpauth.models import LdapUser, ActivationToken, LdapGroup from warpauth.util import * from django.utils.translation import ugettext as _ from django.core.exceptions import ObjectDoesNotExist - +from warpzone.settings import INSTANCE_NAME +from django.shortcuts import redirect def send_token(token): email_content = _("https://infra.warpzone.ms/account/registration/activate/%(token)s") % {'token': token.token } @@ -20,6 +21,8 @@ def send_token(token): def register(request): page_context['success'] = False + if "INTERN" in INSTANCE_NAME: + return redirect('http://infra.warpzone.ms/account/registration/') if request.method == "POST": username = request.POST['username'] @@ -73,6 +76,8 @@ def register(request): def activate(request, token=None): + if "INTERN" in INSTANCE_NAME: + return redirect('http://infra.warpzone.ms/account/registration/') try: activation_token = ActivationToken.objects.get(token=token) group = LdapGroup.objects.filter(name="active").get() @@ -85,6 +90,8 @@ def activate(request, token=None): return HttpResponse(render(request, 'warpauth/register/activate.html', page_context)) def resend_token(request): + if "INTERN" in INSTANCE_NAME: + return redirect('http://infra.warpzone.ms/account/registration/') if request.method == "POST": try: token = ActivationToken.objects.get(user=request.POST["username"]) diff --git a/www/web/warpauth/views/reset_password.py b/www/web/warpauth/views/reset_password.py index 15b6d799e7aef2ca91aa62320b6efc72e7ad443b..ade8aeff8f93d7f0f3d8af92039cf78cc789dfff 100644 --- a/www/web/warpauth/views/reset_password.py +++ b/www/web/warpauth/views/reset_password.py @@ -13,14 +13,17 @@ from warpzone.util import send_email from warpauth.util import * from warpauth.models import PasswordResetToken, LdapUser - -from warpzone.settings import PW_RESET_TOKEN_LIFETIME +from django.shortcuts import redirect +from warpzone.settings import PW_RESET_TOKEN_LIFETIME, INSTANCE_NAME # # Function to generate a password reset Token # def gen_token(request): + if "INTERN" in INSTANCE_NAME: + return redirect('http://infra.warpzone.ms/account/reset_password/') + if request.POST: try: usr = LdapUser.objects.get(uid=request.POST["username"]) @@ -45,6 +48,8 @@ def gen_token(request): # def change_password(request, reset_hash=None): + if "INTERN" in INSTANCE_NAME: + return redirect('http://infra.warpzone.ms/account/reset_password/') try: pw_reset_token = PasswordResetToken.objects.get(hash=reset_hash) time_difference = datetime.datetime.now() - pw_reset_token.created diff --git a/www/web/warppay/models.py b/www/web/warppay/models.py index 17ad85cda0abae03dcad676efe64e4d6d4ed5d93..8c13af91e5cf9607dc5e8ec9f5a5c23e3f292655 100644 --- a/www/web/warppay/models.py +++ b/www/web/warppay/models.py @@ -6,21 +6,23 @@ from warpauth.models import LdapUser class ProductCategory(models.Model): name = models.CharField(max_length=100, unique=True) + position = models.FloatField(null=True, blank=True, default=0) def __str__(self): return self.name class ProductCategorySerializer(serializers.ModelSerializer): class Meta: model = ProductCategory - fields = ['id', 'name'] + fields = ['id', 'name','position'] class Product(models.Model): name = models.CharField(max_length=100, null=True) price_ek = models.FloatField() price_vk = models.FloatField() category = models.ForeignKey(ProductCategory, on_delete=models.CASCADE, null=True) - stock_count = models.IntegerField() - barcode = models.CharField(max_length=100, null=True) + stock_count = models.IntegerField(null=True, blank=True, default=0) + barcode = models.CharField(max_length=100, null=True, blank=True) + position = models.FloatField(null=True, blank=True, default=0) def __str__(self): return self.name @@ -28,9 +30,10 @@ class Product(models.Model): class ProductSerializer(serializers.ModelSerializer): category = serializers.StringRelatedField() + class Meta: model = Product - fields = ['id', 'name', 'price_vk', 'category', 'stock_count'] + fields = ['id', 'name', 'price_vk', 'category', 'barcode','stock_count','position'] class Transaction(models.Model): @@ -53,6 +56,7 @@ class UserCredit(models.Model): uid = models.CharField(max_length=100,unique=True) card_id = models.CharField(max_length=10, null=True) # Unique only with django 1.11 credit = models.FloatField() + pinCode = models.CharField(max_length=100, null=True) def __str__(self): return self.uid @@ -61,4 +65,4 @@ class UserCredit(models.Model): class UserCreditSerializer(serializers.ModelSerializer): class Meta: model = UserCredit - fields = ['uid', 'card_id', 'credit'] + fields = ['uid', 'card_id', 'credit', 'pinCode'] diff --git a/www/web/warppay/urls.py b/www/web/warppay/urls.py index 626d2384e6a353a7723a2f8caa6c97c5112b1d15..15e284c5e2cfd4a98e4e2f5bd93b7a893102b9f5 100644 --- a/www/web/warppay/urls.py +++ b/www/web/warppay/urls.py @@ -7,7 +7,8 @@ urlpatterns = [ url(r'^api/users/(?P<user_id>\w+)/$', views.user_list), url(r'^api/products/$', views.product_list), url(r'^api/categories/$', views.category_list), - url(r'^api/gen_token/$', views.gen_token), - url(r'^api/transaction/(?P<user_id>\w+)/$', views.transaction), + # url(r'^api/gen_token/$', views.gen_token), + url(r'^api/transactions/(?P<user_id>\w+)/$', views.multiple_transaction), + url(r'^api/products/(?P<prod_id>\w+)/barcode/$', views.addBarcode), ] diff --git a/www/web/warppay/views.py b/www/web/warppay/views.py index 6ea282f392fe76f41af524e7ad8778dc0cbd5756..886fcb0c0bace26218048298bd4b4ddbef5383b0 100644 --- a/www/web/warppay/views.py +++ b/www/web/warppay/views.py @@ -13,30 +13,46 @@ from rest_framework import status # logging.getLogger('main').info(token.key) -@api_view(['GET', 'PUT']) -#@authentication_classes((TokenAuthentication,)) -#@permission_classes((IsAuthenticated,)) +@api_view(['GET']) +@authentication_classes((TokenAuthentication,)) +@permission_classes((IsAuthenticated,)) def category_list(request): if request.method == 'GET': products = ProductCategory.objects.all() - serializer = ProductCategorySerializer(products,context={'request': request}, many=True) + serializer = ProductCategorySerializer(products, context={'request': request}, many=True) return Response(serializer.data) return Response() @api_view(['GET', 'PUT']) -#@authentication_classes((TokenAuthentication,)) -#@permission_classes((IsAuthenticated,)) +@authentication_classes((TokenAuthentication,)) +@permission_classes((IsAuthenticated,)) def product_list(request): if request.method == 'GET': products = Product.objects.all() - serializer = ProductSerializer(products,context={'request': request}, many=True) + serializer = ProductSerializer(products, context={'request': request}, many=True) return Response(serializer.data) elif request.method == 'PUT': return Response() return Response() +@api_view(['PUT']) +def addBarcode(request, prod_id=0): + if request.method == 'PUT': + try: + product = Product.objects.get(id=prod_id) + except ObjectDoesNotExist: + return Response(status=status.HTTP_404_NOT_FOUND) + if not product.barcode: + product.barcode = request.data['barcode'] + product.save() + return Response() + else: + return Response(status=status.HTTP_406_NOT_ACCEPTABLE) + else: + return Response(status=status.HTTP_406_NOT_ACCEPTABLE) + @api_view(['GET']) def gen_token(request): @@ -46,8 +62,8 @@ def gen_token(request): return Response() @api_view(['GET', 'PUT']) -#@authentication_classes((TokenAuthentication,)) -#@permission_classes((IsAuthenticated,)) +@authentication_classes((TokenAuthentication,)) +@permission_classes((IsAuthenticated,)) def user_list(request, user_id = 0): if request.method == 'GET': sync_users() @@ -99,13 +115,12 @@ def user_list(request, user_id = 0): return Response(serializer.data, status=state) return Response() - @api_view(['PUT']) -#@authentication_classes((TokenAuthentication,)) -#@permission_classes((IsAuthenticated,)) -def transaction(request, user_id=None): +@authentication_classes((TokenAuthentication,)) +@permission_classes((IsAuthenticated,)) +def multiple_transaction(request, user_id=None): if request.method == 'PUT': - if 'trans_type' not in request.data or not user_id: + if not user_id: return Response(status=status.HTTP_406_NOT_ACCEPTABLE) try: @@ -113,45 +128,66 @@ def transaction(request, user_id=None): except ObjectDoesNotExist: return Response(status=status.HTTP_404_NOT_FOUND) + transactions = [] + products = [] + for transact in request.data: + if 'trans_type' not in transact: + return Response(status=status.HTTP_406_NOT_ACCEPTABLE) + t = Transaction() - t.type = int(request.data['trans_type']) + t.type = int(transact['trans_type']) if t.type == 1: - if 'amount' not in request.data or ('amount' in request.data and float(request.data['amount'] < 0)): + if 'amount' not in transact or ('amount' in transact and float(transact['amount'] < 0)): return Response(status=status.HTTP_406_NOT_ACCEPTABLE) - if 'cash_paid' in request.data: - t.cash_paid = bool(request.data['cash_paid']) + if 'cash_paid' in transact: + t.cash_paid = bool(transact['cash_paid']) else: t.cash_paid = True - t.amount = float(request.data['amount']) - u.credit +=t.amount + t.amount = float(transact['amount']) + u.credit += t.amount elif t.type == 2: try: - product = Product.objects.get(id=request.data['product']['id']) + product = Product.objects.get(id=transact['product']['id']) t.product = product - if 'cash_paid' in request.data: - t.cash_paid = bool(request.data['cash_paid']) + if 'cash_paid' in transact: + t.cash_paid = bool(transact['cash_paid']) t.amount = product.price_vk if not t.cash_paid: u.credit -= t.amount + product.stock_count -= 1 + products.append(product) + except: return Response(status=status.HTTP_406_NOT_ACCEPTABLE) else: return Response(status=status.HTTP_406_NOT_ACCEPTABLE) + transactions.append(t) + + for t in transactions: t.save() - u.save() - return Response() + print(t) + for p in products: + p.save() + print(p) + + u.save() + print(u) + return Response() def sync_users(): - for user in LdapUser.objects.all(): + for ldapuser in LdapUser.objects.all(): try: - u = UserCredit.objects.get(uid=user.uid) - if user.card_id: - u.card_id = user.card_id + u = UserCredit.objects.get(uid=ldapuser.uid) + if ldapuser.card_id: + u.card_id = ldapuser.card_id + u.save() + if ldapuser.pinCode: + u.pinCode = ldapuser.pinCode u.save() except ObjectDoesNotExist: - u = UserCredit(uid=user.uid, card_id=user.card_id, credit=0.0) + u = UserCredit(uid=ldapuser.uid, card_id=ldapuser.card_id, pinCode="", credit=0.0) u.save() diff --git a/www/web/warpzone/settings.py b/www/web/warpzone/settings.py index b0f7bb2619149f419d8981f987878bae98e620a7..d9e6d65eaec37b4399373c08293a0cca6d2cacee 100644 --- a/www/web/warpzone/settings.py +++ b/www/web/warpzone/settings.py @@ -52,10 +52,11 @@ SECRET_KEY = config.get('security','SECRET_KEY') API_KEY = config.get('mattermost','API_KEY') # DEBUG -DEBUG = config.getboolean('debug','DEBUG') +DEBUG = config.getboolean('debug', 'DEBUG') # MISC -LOG_PATH = config.get('misc','LOG_PATH') +LOG_PATH = config.get('misc', 'LOG_PATH') +INSTANCE_NAME = config.get('common', 'INSTANCE_NAME') ALLOWED_HOSTS = [config.get('security','ALLOWED_HOSTS')]