A lot of code moved to ungleich-common

This commit is contained in:
ahmadbilalkhalid 2020-02-20 00:12:11 +05:00
parent ce709c3b6f
commit cee92f2e99
6 changed files with 18 additions and 268 deletions

View file

@ -1,23 +1,14 @@
import configparser
import sys
import os import os
from etcd_wrapper import EtcdWrapper from ungleich_common.etcd_wrapper import EtcdWrapper
from ldap_manager import LdapManager from ungleich_common.ldap_manager import LdapManager
from ungleich_common.config_parser import StrictConfigParser
config_file = os.environ.get('meow-pay-config-file', default='pay.conf') config_file = os.environ.get('meow-pay-config-file', default='pay.conf')
config = configparser.ConfigParser(allow_no_value=True) config = StrictConfigParser(allow_no_value=True)
config.read(config_file)
try:
successfully_read_files = config.read(config_file)
except configparser.Error as err:
sys.exit(err)
if not successfully_read_files:
sys.exit(f'Config file {config_file} couldn\'t be read.')
try:
etcd_client = EtcdWrapper( etcd_client = EtcdWrapper(
host=config.get('etcd', 'host'), port=config.get('etcd', 'port'), host=config.get('etcd', 'host'), port=config.get('etcd', 'port'),
ca_cert=config.get('etcd', 'ca_cert'), cert_key=config.get('etcd', 'cert_key'), ca_cert=config.get('etcd', 'ca_cert'), cert_key=config.get('etcd', 'cert_key'),
@ -28,5 +19,3 @@ try:
server=config.get('ldap', 'server'), admin_dn=config.get('ldap', 'admin_dn'), server=config.get('ldap', 'server'), admin_dn=config.get('ldap', 'admin_dn'),
admin_password=config.get('ldap', 'admin_password') admin_password=config.get('ldap', 'admin_password')
) )
except configparser.Error as err:
sys.exit(f'{err} in config file {config_file}.')

View file

@ -1,75 +0,0 @@
import etcd3
import json
import logging
from functools import wraps
class EtcdEntry:
def __init__(self, meta_or_key, value, value_in_json=True):
if hasattr(meta_or_key, 'key'):
# if meta has attr 'key' then get it
self.key = meta_or_key.key.decode('utf-8')
else:
# otherwise meta is the 'key'
self.key = meta_or_key
self.value = value.decode('utf-8')
if value_in_json:
self.value = json.loads(self.value)
def readable_errors(func):
@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except etcd3.exceptions.ConnectionFailedError as err:
raise etcd3.exceptions.ConnectionFailedError(
'Cannot connect to etcd: is etcd running as configured in uncloud.conf?'
) from err
except etcd3.exceptions.ConnectionTimeoutError as err:
raise etcd3.exceptions.ConnectionTimeoutError('etcd connection timeout.') from err
except Exception:
logging.exception('Some etcd error occured. See syslog for details.')
return wrapper
class EtcdWrapper:
@readable_errors
def __init__(self, *args, **kwargs):
self.client = etcd3.client(*args, **kwargs)
@readable_errors
def get(self, *args, value_in_json=True, **kwargs):
_value, _key = self.client.get(*args, **kwargs)
if _key is None or _value is None:
return None
return EtcdEntry(_key, _value, value_in_json=value_in_json)
@readable_errors
def put(self, *args, value_in_json=True, **kwargs):
_key, _value = args
if value_in_json:
_value = json.dumps(_value)
if not isinstance(_key, str):
_key = _key.decode('utf-8')
return self.client.put(_key, _value, **kwargs)
@readable_errors
def get_prefix(self, *args, value_in_json=True, **kwargs):
event_iterator = self.client.get_prefix(*args, **kwargs)
for e in event_iterator:
yield EtcdEntry(*e[::-1], value_in_json=value_in_json)
@readable_errors
def watch_prefix(self, key, value_in_json=True):
event_iterator, cancel = self.client.watch_prefix(key)
for e in event_iterator:
if hasattr(e, '_event'):
e = getattr('e', '_event')
if e.type == e.PUT:
yield EtcdEntry(e.kv.key, e.kv.value, value_in_json=value_in_json)

View file

@ -1,76 +0,0 @@
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)
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'):
kwargs = {
'connection': self.conn,
'object_def': self.person_obj_def,
'base': search_base,
}
if query:
kwargs['query'] = query
r = Reader(**kwargs)
return r.search()
def is_password_valid(self, query_value, password, query_key='mail', **kwargs):
entries = self.get(query='({}={})'.format(query_key, query_value), **kwargs)
if entries:
password_in_ldap = entries[0].userPassword.value
found = self._check_password(password_in_ldap, password)
if not found:
raise Exception('Invalid Password')
else:
return entries[0]
else:
raise ValueError('Such {}={} not found'.format(query_key, query_value))
@staticmethod
def _check_password(tagged_digest_salt, password):
digest_salt_b64 = tagged_digest_salt[6:]
digest_salt = base64.decodebytes(digest_salt_b64)
digest = digest_salt[:20]
salt = digest_salt[20:]
sha = hashlib.sha1(password.encode('utf-8'))
sha.update(salt)
return digest == sha.digest()
@staticmethod
def ssha_password(password):
"""
Apply the SSHA password hashing scheme to the given *password*.
*password* must be a :class:`bytes` object, containing the utf-8
encoded password.
Return a :class:`bytes` object containing ``ascii``-compatible data
which can be used as LDAP value, e.g. after armoring it once more using
base64 or decoding it to unicode from ``ascii``.
"""
sha1 = hashlib.sha1()
salt = random.SystemRandom().getrandbits(SALT_BYTES * 8).to_bytes(SALT_BYTES, 'little')
sha1.update(password)
sha1.update(salt)
digest = sha1.digest()
passwd = b'{SSHA}' + base64.b64encode(digest + salt)
return passwd

View file

@ -2,3 +2,4 @@ ldap3
etcd3 etcd3
stripe stripe
flask flask
git+git://code.ungleich.ch/ahmedbilal/ungleich-common@master#egg=ungleich-common

View file

@ -5,91 +5,7 @@ import math
from config import ldap_manager, etcd_client from config import ldap_manager, etcd_client
from helper import resolve_product from helper import resolve_product
from ungleich_common.schemas import BaseSchema, Field, ValidationException
class ValidationException(Exception):
"""Validation Error"""
class Field:
def __init__(self, _name, _type, _value=None, validators=None, disable_validation=False):
self.validation_disabled = disable_validation
self.name = _name
self.value = _value
self.type = _type
self.validators = validators or []
def is_valid(self):
if not self.validation_disabled:
if not isinstance(self.value, self.type):
try:
self.value = self.type(self.value)
except Exception:
raise ValidationException("Incorrect Type for '{}' field".format(self.name))
for validator in self.validators:
validator()
def __repr__(self):
return self.name
class BaseSchema:
def __init__(self):
self.objects = {}
def validation(self):
# custom validation is optional
return True
def get_fields(self):
return [getattr(self, field) for field in dir(self) if isinstance(getattr(self, field), Field)]
def is_valid(self):
for field in self.get_fields():
field.is_valid()
self.validation()
def get_cleaned_values(self):
field_kv_dict = {
field.name: field.value
for field in self.get_fields()
}
cleaned_values = field_kv_dict
cleaned_values.update(self.objects)
return cleaned_values
def add_schema(self, schema, data, under_field_name=None):
s = schema(data)
s.is_valid()
base = self
if under_field_name:
# Create a field in self
setattr(self, under_field_name, Field(under_field_name, dict, _value={}, disable_validation=True))
base = getattr(self, under_field_name)
for field in s.get_fields():
if under_field_name:
getattr(base, 'value')[field.name] = field.value
else:
setattr(base, field.name, field)
self.objects.update(s.objects)
@staticmethod
def get(dictionary: dict, key: str, return_default=False, default=None):
if dictionary is None:
raise ValidationException('No data provided at all.')
try:
value = dictionary[key]
except KeyError:
if return_default:
return {'_value': default, 'disable_validation': True}
raise ValidationException("Missing data for '{}' field.".format(key))
else:
return {'_value': value, 'disable_validation': False}
class AddProductSchema(BaseSchema): class AddProductSchema(BaseSchema):

View file

@ -2,15 +2,10 @@ import re
import stripe import stripe
import stripe.error import stripe.error
import logging import logging
import sys
from configparser import Error as ConfigParserError from config import etcd_client as client, config as config
from config import etcd_client as client, config as config, config_file
try:
stripe.api_key = config.get('stripe', 'private_key') 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): def handle_stripe_error(f):