213 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			213 lines
		
	
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from flask import Flask, request
 | 
						|
from flask_restful import Resource, Api
 | 
						|
import etcd3
 | 
						|
import json
 | 
						|
import logging
 | 
						|
from functools import wraps
 | 
						|
 | 
						|
from ldaptest import is_valid_ldap_user
 | 
						|
 | 
						|
def authenticate(func):
 | 
						|
    @wraps(func)
 | 
						|
    def wrapper(*args, **kwargs):
 | 
						|
        if not getattr(func, 'authenticated', True):
 | 
						|
            return func(*args, **kwargs)
 | 
						|
 | 
						|
        # pass in username/password !
 | 
						|
        acct = basic_authentication()  # custom account lookup function
 | 
						|
 | 
						|
        if acct:
 | 
						|
            return func(*args, **kwargs)
 | 
						|
 | 
						|
        flask_restful.abort(401)
 | 
						|
    return wrapper
 | 
						|
 | 
						|
def readable_errors(func):
 | 
						|
    @wraps(func)
 | 
						|
    def wrapper(*args, **kwargs):
 | 
						|
        try:
 | 
						|
            return func(*args, **kwargs)
 | 
						|
        except etcd3.exceptions.ConnectionFailedError as e:
 | 
						|
            raise UncloudException('Cannot connect to etcd: is etcd running and reachable? {}'.format(e))
 | 
						|
        except etcd3.exceptions.ConnectionTimeoutError as e:
 | 
						|
            raise UncloudException('etcd connection timeout. {}'.format(e))
 | 
						|
 | 
						|
    return wrapper
 | 
						|
 | 
						|
 | 
						|
class DB(object):
 | 
						|
    def __init__(self, config, prefix="/"):
 | 
						|
        self.config = config
 | 
						|
 | 
						|
        # Root for everything
 | 
						|
        self.base_prefix= '/nicohack'
 | 
						|
 | 
						|
        # Can be set from outside
 | 
						|
        self.prefix = prefix
 | 
						|
 | 
						|
        self.connect()
 | 
						|
 | 
						|
    @readable_errors
 | 
						|
    def connect(self):
 | 
						|
        self._db_clients = []
 | 
						|
        for endpoint in self.config.etcd_hosts:
 | 
						|
            client = etcd3.client(host=endpoint, **self.config.etcd_args)
 | 
						|
            self._db_clients.append(client)
 | 
						|
 | 
						|
    def realkey(self, key):
 | 
						|
        return "{}{}/{}".format(self.base_prefix,
 | 
						|
                                self.prefix,
 | 
						|
                                key)
 | 
						|
 | 
						|
    @readable_errors
 | 
						|
    def get(self, key, as_json=False, **kwargs):
 | 
						|
        value, _ = self._db_clients[0].get(self.realkey(key), **kwargs)
 | 
						|
 | 
						|
        if as_json:
 | 
						|
            value = json.loads(value)
 | 
						|
 | 
						|
        return value
 | 
						|
 | 
						|
 | 
						|
    @readable_errors
 | 
						|
    def set(self, key, value, as_json=False, **kwargs):
 | 
						|
        if as_json:
 | 
						|
            value = json.dumps(value)
 | 
						|
 | 
						|
        # FIXME: iterate over clients in case of failure ?
 | 
						|
        return self._db_clients[0].put(self.realkey(key), value, **kwargs)
 | 
						|
 | 
						|
 | 
						|
class Membership(Resource):
 | 
						|
    def __init__(self, config):
 | 
						|
        self.config = config
 | 
						|
 | 
						|
    def get(self):
 | 
						|
        data = request.get_json(silent=True) or {}
 | 
						|
        print("{} {}".format(data, config))
 | 
						|
        return {'message': 'Order successful' }, 200
 | 
						|
 | 
						|
    def post(self):
 | 
						|
        data = request.get_json(silent=True) or {}
 | 
						|
        print("{} {}".format(data, config))
 | 
						|
        return {'message': 'Order 2x successful' }, 200
 | 
						|
 | 
						|
 | 
						|
class Order(Resource):
 | 
						|
    def __init__(self, config):
 | 
						|
        self.config = config
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def post():
 | 
						|
        data = request.get_json(silent=True) or {}
 | 
						|
        print("{} {}".format(data, config))
 | 
						|
 | 
						|
 | 
						|
class Product(Resource):
 | 
						|
    def __init__(self, config):
 | 
						|
        self.config = config
 | 
						|
 | 
						|
        self.products = []
 | 
						|
        self.products.append(
 | 
						|
            { "name": "membership-free",
 | 
						|
              "description": """
 | 
						|
This membership gives you access to the API and includes a VPN
 | 
						|
with 1 IPv6 address.
 | 
						|
See https://redmine.ungleich.ch/issues/7747?
 | 
						|
""",
 | 
						|
              "uuid": "a3883466-0012-4d01-80ff-cbf7469957af",
 | 
						|
              "recurring": True,
 | 
						|
              "recurring_time_frame": "per_year",
 | 
						|
              "features": [
 | 
						|
                  { "name": "membership",
 | 
						|
                    "price_one_time": 0,
 | 
						|
                    "price_recurring": 0
 | 
						|
                  }
 | 
						|
              ]
 | 
						|
            }
 | 
						|
        )
 | 
						|
        self.products.append(
 | 
						|
            { "name": "membership-standard",
 | 
						|
              "description": """
 | 
						|
This membership gives you access to the API and includes an IPv6-VPN with
 | 
						|
one IPv6 address ("Road warrior")
 | 
						|
See https://redmine.ungleich.ch/issues/7747?
 | 
						|
""",
 | 
						|
              "uuid": "1d85296b-0863-4dd6-a543-a6d5a4fbe4a6",
 | 
						|
              "recurring": True,
 | 
						|
              "recurring_time_frame": "per_month",
 | 
						|
              "features": [
 | 
						|
                  { "name": "membership",
 | 
						|
                    "price_one_time": 0,
 | 
						|
                    "price_recurring": 5
 | 
						|
                  }
 | 
						|
 | 
						|
              ]
 | 
						|
            }
 | 
						|
        )
 | 
						|
        self.products.append(
 | 
						|
            { "name": "membership-premium",
 | 
						|
              "description": """
 | 
						|
This membership gives you access to the API and includes an
 | 
						|
IPv6-VPN with a /48 IPv6 network.
 | 
						|
See https://redmine.ungleich.ch/issues/7747?
 | 
						|
""",
 | 
						|
              "uuid": "bfd63fd2-d227-436f-a8b8-600de74dd6ce",
 | 
						|
              "recurring": True,
 | 
						|
              "recurring_time_frame": "per_month",
 | 
						|
              "features": [
 | 
						|
                  { "name": "membership",
 | 
						|
                    "price_one_time": 0,
 | 
						|
                    "price_recurring": 5
 | 
						|
                  }
 | 
						|
 | 
						|
              ]
 | 
						|
            }
 | 
						|
        )
 | 
						|
        self.products.append(
 | 
						|
            { "name": "ipv6-vpn-with-/48",
 | 
						|
              "description": """
 | 
						|
An IPv6 VPN with a /48 network included.
 | 
						|
""",
 | 
						|
              "uuid": "fe5753f8-6fe1-4dc4-9b73-7b803de4c597",
 | 
						|
              "recurring": True,
 | 
						|
              "recurring_time_frame": "per_year",
 | 
						|
              "features": [
 | 
						|
                  { "name": "vpn",
 | 
						|
                    "price_one_time": 0,
 | 
						|
                    "price_recurring": 120
 | 
						|
                  }
 | 
						|
              ]
 | 
						|
            }
 | 
						|
        )
 | 
						|
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def post():
 | 
						|
        data = request.get_json(silent=True) or {}
 | 
						|
        print("{} {}".format(data, config))
 | 
						|
 | 
						|
    def get(self):
 | 
						|
        data = request.get_json(silent=True) or {}
 | 
						|
        print("{} {}".format(data, config))
 | 
						|
 | 
						|
        return self.products
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    app = Flask(__name__)
 | 
						|
 | 
						|
    config = {}
 | 
						|
 | 
						|
    config['etcd_url']="https://etcd1.ungleich.ch"
 | 
						|
    config['ldap_url']="ldaps://ldap1.ungleich.ch"
 | 
						|
 | 
						|
    api = Api(app)
 | 
						|
    api.add_resource(Order, '/orders', resource_class_args=( config, ))
 | 
						|
    api.add_resource(Product, '/products', resource_class_args=( config, ))
 | 
						|
    api.add_resource(Membership, '/membership',  resource_class_args=( config, ))
 | 
						|
 | 
						|
    app.run(host='::', port=5000, debug=True)
 |