diff --git a/.gitignore b/.gitignore index 77de841..304c492 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,4 @@ __pycache__/ pay.conf -log.txt -test.py \ No newline at end of file +log.txt \ No newline at end of file diff --git a/README.md b/README.md index 1b50cf3..6dae6b9 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Currently handles very basic features, such as: #### 1. Adding of products ```shell script -http --json http://[::]:5000/product/add email=your_email_here password=your_password_here specs:=@ipv6-only-vm.json +http --json http://[::]:5000/product/add username=your_username_here password=your_password_here specs:=@ipv6-only-vm.json ``` #### 2. Listing of products @@ -24,20 +24,26 @@ http --json http://[::]:5000/product/add email=your_email_here password=your_pas http --json http://[::]:5000/product/list ``` -#### 3. Ordering products +#### 3. Registering user's payment method (credit card for now using Stripe) + ```shell script -http --json http://[::]:5000/product/order email=your_email_here password=your_password_here product_id=5332cb89453d495381e2b2167f32c842 cpu=1 ram=1gb os-disk-space=10gb os=alpine +http --json http://[::]:5000/user/register_payment card_number=4111111111111111 cvc=123 expiry_year=2020 expiry_month=8 card_holder_name="The test user" username=your_username_here password=your_password_here line1="your_billing_address" city="your_city" country="your_country" ``` -#### 4. Listing users orders +#### 4. Ordering products + +First of all, user have to buy the membership first. ```shell script -http --json GET http://[::]:5000/order/list email=your_email_here password=your_password_here +http --json http://[::]:5000/product/order username=your_username_here password=your_password_here product_id=membership pay=True ``` +```shell script +http --json http://[::]:5000/product/order username=your_username_here password=your_password_here product_id=ipv6-only-vm cpu=1 ram=1 os-disk-space=10 os=alpine pay=True +``` -#### 5. Registering user's payment method (credit card for now using Stripe) +#### 5. Listing users orders ```shell script -http --json http://[::]:5000/user/register_payment card_number=4111111111111111 cvc=123 expiry_year=2020 expiry_month=8 card_holder_name="The test user" email=your_email_here password=your_password_here -``` \ No newline at end of file +http --json POST http://[::]:5000/order/list username=your_username_here password=your_password_here +``` diff --git a/config.py b/config.py index b951830..4d5e16a 100644 --- a/config.py +++ b/config.py @@ -1,11 +1,28 @@ import configparser +import sys +import os + from etcd_wrapper import EtcdWrapper from ldap_manager import LdapManager +config_file = os.environ.get('meow-pay-config-file', default='pay.conf') + config = configparser.ConfigParser() -config.read('pay.conf') -etcd_client = EtcdWrapper(host=config['etcd']['host'], port=config['etcd']['port']) +try: + successfully_read_files = config.read(config_file) +except configparser.Error as err: + sys.exit(err) -ldap_manager = LdapManager(server=config['ldap']['server'], admin_dn=config['ldap']['admin_dn'], - admin_password=config['ldap']['admin_password']) \ No newline at end of file +if not successfully_read_files: + sys.exit(f'Config file {config_file} couldn\'t be read.') + +try: + etcd_client = EtcdWrapper(host=config.get('etcd', 'host'), port=config.get('etcd', 'port')) + + ldap_manager = LdapManager( + server=config.get('ldap', 'server'), admin_dn=config.get('ldap', 'admin_dn'), + admin_password=config.get('ldap', 'admin_password') + ) +except configparser.Error as err: + sys.exit(f'{err} in config file {config_file}.') diff --git a/ldap_manager.py b/ldap_manager.py index 382afab..c0a793f 100644 --- a/ldap_manager.py +++ b/ldap_manager.py @@ -1,14 +1,22 @@ import hashlib import random import base64 +import sys from ldap3 import Server, Connection, ObjectDef, Reader, ALL +from ldap3.core import exceptions + +SALT_BYTES = 15 class LdapManager: def __init__(self, server, admin_dn, admin_password): self.server = Server(server, get_info=ALL) - self.conn = Connection(server, admin_dn, admin_password, auto_bind=True) + try: + self.conn = Connection(server, admin_dn, admin_password, auto_bind=True) + except exceptions.LDAPException as err: + sys.exit(f'LDAP Error: {err}') + self.person_obj_def = ObjectDef('inetOrgPerson', self.conn) def get(self, query=None, search_base='dc=ungleich,dc=ch'): @@ -57,7 +65,6 @@ class LdapManager: which can be used as LDAP value, e.g. after armoring it once more using base64 or decoding it to unicode from ``ascii``. """ - SALT_BYTES = 15 sha1 = hashlib.sha1() salt = random.SystemRandom().getrandbits(SALT_BYTES * 8).to_bytes(SALT_BYTES, 'little') diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..843641e --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +ldap3 +etcd3 +stripe +flask diff --git a/sample-pay.conf b/sample-pay.conf index bed5dbe..7138838 100644 --- a/sample-pay.conf +++ b/sample-pay.conf @@ -12,8 +12,3 @@ port = 5000 server = ldap_server_url admin_dn = ldap_admin_dn admin_password = ldap_admin_password -customer_dn = ldap_customer_dn -user_dn = ldap_user_dn - -internal_user_ou = users -customer_ou = customer \ No newline at end of file diff --git a/schemas.py b/schemas.py index 106b591..25555f9 100644 --- a/schemas.py +++ b/schemas.py @@ -3,11 +3,9 @@ import config import json import math -from config import ldap_manager +from config import ldap_manager, etcd_client from helper import resolve_product -etcd_client = config.etcd_client - class ValidationException(Exception): """Validation Error""" @@ -105,7 +103,7 @@ class AddProductSchema(BaseSchema): user = self.objects['user'] user = json.loads(user.entry_to_json()) uid, ou, *dc = user['dn'].replace('ou=', '').replace('dc=', '').replace('uid=', '').split(',') - if ou != config.config['ldap']['internal_user_ou']: + if ou != config.config.get('ldap', 'internal_user_ou', fallback='users'): raise ValidationException('You do not have access to create product.') product = resolve_product(self.specs.value['usable-id'], etcd_client) diff --git a/stripe_utils.py b/stripe_utils.py index 9474f74..1004b86 100644 --- a/stripe_utils.py +++ b/stripe_utils.py @@ -1,12 +1,16 @@ -import json import re import stripe import stripe.error import logging +import sys -from config import etcd_client as client, config as config +from configparser import Error as ConfigParserError +from config import etcd_client as client, config as config, config_file -stripe.api_key = config['stripe']['private_key'] +try: + stripe.api_key = config.get('stripe', 'private_key') +except ConfigParserError as err: + sys.exit(f'{err} in config file {config_file}') def handle_stripe_error(f): @@ -291,7 +295,7 @@ class StripeUtils(object): returns the new object. :param amount: The amount in CHF cents - :param name: The name of the Stripe plan to be created. + :param product_name: The name of the Stripe plan (product) to be created. :param stripe_plan_id: The id of the Stripe plan to be created. Use get_stripe_plan_id_string function to obtain the name of the plan to be created diff --git a/ucloud_pay.py b/ucloud_pay.py index 09c5813..fc45951 100644 --- a/ucloud_pay.py +++ b/ucloud_pay.py @@ -5,7 +5,7 @@ from uuid import uuid4 from flask import Flask, request from flask_restful import Resource, Api - +from werkzeug.exceptions import HTTPException from config import etcd_client as client, config as config from stripe_utils import StripeUtils from schemas import ( @@ -322,4 +322,15 @@ if __name__ == '__main__': api.add_resource(UserRegisterPayment, '/user/register_payment') api.add_resource(OrderList, '/order/list') - app.run(host='::', port=config['app']['port'], debug=True) + app.run(host='::', port=config.get('app', 'port', fallback=5000), debug=True) + + + @app.errorhandler(Exception) + def handle_exception(e): + app.logger.error(e) + # pass through HTTP errors + if isinstance(e, HTTPException): + return e + + # now you're handling non-HTTP exceptions only + return {'message': 'Server Error'}, 500