Commit 1f5b8481 authored by Gero Müller's avatar Gero Müller
Browse files

rename to ldap_export, add comamnd line tool to sync ldap, refactor server.py

parent b21185d9
#!/usr/bin/env python
import sys, os
base = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
if os.path.isdir(base):
sys.path.insert(0, base)
from argparse import ArgumentParser
import vispa
from vispa.extensions.ldap_export import LDAPExport
from vispa import MAJOR_VERSION, MINOR_VERSION
from sqlalchemy.orm import scoped_session, sessionmaker
import logging
logging.basicConfig(level=logging.INFO)
parser = ArgumentParser()
default_base = os.path.expanduser("~/.vispa-%s.%s" % (MAJOR_VERSION, MINOR_VERSION))
parser.add_argument('-c', '--config-dir', dest='configdir',
default=os.path.join(default_base, "conf"),
help='Directory containing configuration files '
'(default: %s)' % os.path.join(default_base, "conf"))
parser.add_argument('-d', '--data-dir', dest='vardir',
default=os.path.join(default_base, "var"),
help='Directory containing server '
'data and cache files (default: %s)'
% os.path.join(default_base, "var"))
parser.add_argument('--delete',
default=False,
help='Delete unknown users and groups')
args = parser.parse_args()
vispa.setup_config(os.path.abspath(args.configdir))
db = vispa.models.open_database(args.vardir)
session = scoped_session(sessionmaker(autoflush=True, autocommit=False))
session.configure(bind=db)
ldapexport = LDAPExport()
if ldapexport.connect():
session = scoped_session(sessionmaker(autoflush=True, autocommit=False))
session.configure(bind=self.server._engine)
if args.delete:
self.delete_unknown(session)
self.sync_all_users(session)
......@@ -82,6 +82,28 @@ def set_codepath(p):
_codepath = p
def config_files(directory):
conf_d_dir = configpath(directory)
if os.path.isdir(conf_d_dir):
config_files = [f for f in os.listdir(conf_d_dir) if f.endswith(".ini") and os.path.isfile(os.path.join(conf_d_dir, f))]
config_files.sort()
return [os.path.join(conf_d_dir, f) for f in config_files]
else:
return []
def setup_config(conf_dir):
logger.info('Using %s as config dir.' % conf_dir)
set_configpath(conf_dir)
logger.info('Read config file: %s', configpath('vispa.ini'))
config.read(configpath('vispa.ini'))
# load all ini files
_config_files = config_files('vispa.d')
logger.info('Read config files: %s', _config_files)
config.read(_config_files)
class VispaConfigParser(cp.SafeConfigParser):
def __call__(self, section, option, default=None):
if self.has_option(section, option):
......
......@@ -15,51 +15,36 @@ from ldap3.core.exceptions import LDAPNoSuchObjectResult
logger = logging.getLogger(__name__)
def _r(val):
return [(ldap3.MODIFY_REPLACE, [val])]
class LDAPExportExtension(AbstractExtension):
def name(self):
return "ldap-export"
def dependencies(self):
return []
class LDAPExport(object):
def setup(self):
def __init__(self):
self.user_base = vispa.config("ldap-export", "user_base")
self.group_base = vispa.config("ldap-export", "group_base")
self.uid_offset = vispa.config("ldap-export", "uid_offset", 10000)
self.private_group = vispa.config("ldap-export", "private_group", True)
self.gid_offset = vispa.config("ldap-export", "gid_offset", 100000)
set_library_log_detail_level(BASIC)
def connect(self):
url = vispa.config("ldap-export", "url")
user = vispa.config("ldap-export", "user")
password = vispa.config("ldap-export", "password")
set_library_log_detail_level(BASIC)
if url:
self.connection = ldap3.Connection(url, user, password, auto_bind=True, raise_exceptions=True)
else:
self.connection = None
logger.info(self.connection)
if self.connection:
self.user_base = vispa.config("ldap-export", "user_base")
self.group_base = vispa.config("ldap-export", "group_base")
self.uid_offset = vispa.config("ldap-export", "uid_offset", 10000)
self.private_group = vispa.config("ldap-export", "private_group", True)
self.gid_offset = vispa.config("ldap-export", "gid_offset", 100000)
vispa.register_callback("user.activate", self.user_add)
vispa.register_callback("user.set_password", self.user_set_password)
if vispa.config("ldap-export", "sync_on_startup", False):
if vispa.config("ldap-export", "delete_unknown", False):
self.delete_unknown()
self.sync_all_users()
def delete_unknown(self):
session = scoped_session(sessionmaker(autoflush=True, autocommit=False))
session.configure(bind=self.server._engine)
if not url:
return
self.connection = ldap3.Connection(url, user, password, auto_bind=True, raise_exceptions=True)
return self.connection != None
def delete_unknown(self, db):
try:
active_users = session.query(User, User.name).filter_by(status=User.ACTIVE).all()
active_users = db.query(User, User.name).filter_by(status=User.ACTIVE).all()
self.connection.search(self.user_base, "(objectClass=posixAccount)", attributes=['uid'])
for ldap_user in self.connection.entries:
......@@ -76,13 +61,11 @@ class LDAPExportExtension(AbstractExtension):
except:
logger.exception("remove_invalid_ldap_entries")
finally:
session.remove()
db.remove()
def sync_all_users(self):
session = scoped_session(sessionmaker(autoflush=True, autocommit=False))
session.configure(bind=self.server._engine)
def sync_all_users(self, db):
try:
users = session.query(User).filter_by(status=User.ACTIVE)
users = db.query(User).filter_by(status=User.ACTIVE)
for user in users:
try:
self.user_add(user)
......@@ -91,7 +74,7 @@ class LDAPExportExtension(AbstractExtension):
except:
logger.exception("sync_all_users")
finally:
session.remove()
db.remove()
def user_delete(self, user):
dn = 'cn=%s,%s' % (unicode(user.name), self.user_base)
......@@ -136,12 +119,12 @@ class LDAPExportExtension(AbstractExtension):
self.group_add_member(username, username)
def group_add(self, name, id):
def group_add(self, name, gid):
dn = 'cn=%s,%s' % (name, self.group_base)
classes = ['top', 'posixGroup']
attributes = {
'cn': name,
'gidNumber': id,
'gidNumber': gid,
}
logger.info("Add group: %s, %s, %s", dn, classes, attributes)
try:
......@@ -150,7 +133,7 @@ class LDAPExportExtension(AbstractExtension):
logger.info(" -> updated")
changes = {
'cn': _r(name),
'gidNumber': _r(id),
'gidNumber': _r(gid),
}
self.connection.modify(dn, changes)
......@@ -172,3 +155,28 @@ class LDAPExportExtension(AbstractExtension):
logger.info("Change password: %s, %s", dn, changes)
self.connection.modify(dn, changes)
class LDAPExportExtension(AbstractExtension):
def name(self):
return "ldap-export"
def dependencies(self):
return []
def setup(self):
self.ldapexport = LDAPExport()
if self.ldapexport.connect():
if vispa.config("ldap-export", "sync_on_startup", False):
session = scoped_session(sessionmaker(autoflush=True, autocommit=False))
session.configure(bind=self.server._engine)
if vispa.config("ldap-export", "delete_unknown", False):
self.delete_unknown(session)
self.sync_all_users(session)
vispa.register_callback("user.activate", self.ldapexport.user_add)
vispa.register_callback("user.set_password", self.ldapexport.user_set_password)
\ No newline at end of file
# -*- coding: utf-8 -*-
import os
import logging
import vispa
import sqlalchemy
from sqlalchemy.ext.declarative import declarative_base
logger = logging.getLogger(__name__)
Base = declarative_base()
__all__ = ['group', 'preference', 'project', 'role', 'shortcuts', 'user',
'workgroup', 'workspace']
FORBIDDEN_PHRASES = [u"drop ", u"select ", u"dump ", u"insert ", u"delete ",
u"update ", u"drop\\ ", u"select\\ ", u"dump\\ ",
u"insert\\ ", u"delete\\ ", u"update\\ "]
FORBIDDEN_CHARS = [u"´", u"`"]
def insertion_safe(*args, **kwargs):
for arg in list(args) + kwargs.values():
if isinstance(arg, dict):
......@@ -32,3 +38,32 @@ def insertion_safe(*args, **kwargs):
if elem.lower().find(char) >= 0:
return False, elem
return True, None
def open_database(var_dir=None):
var_dir = var_dir or ""
sa_identifier = vispa.config('database', 'sqlalchemy.url',
'sqlite:///%s' % os.path.join(var_dir, "vispa.db"))
pool_size = vispa.config('database', 'sqlalchemy.pool_size', 10)
max_overflow = vispa.config(
'database',
'sqlalchemy.max_overflow',
10)
# https://github.com/mitsuhiko/flask-sqlalchemy/issues/2
# http://docs.sqlalchemy.org/en/latest/core/pooling.html#dealing-with-disconnects
pool_recycle = vispa.config(
'database',
'sqlalchemy.pool_recycle',
7200)
logger.info('Use database %s.' % sa_identifier)
try:
engine = sqlalchemy.create_engine(
sa_identifier,
echo=False,
pool_size=pool_size,
pool_recycle=pool_recycle,
max_overflow=max_overflow)
except TypeError:
engine = sqlalchemy.create_engine(
sa_identifier,
echo=False)
return engine
\ No newline at end of file
......@@ -138,7 +138,6 @@ class AbstractExtension(object):
view_id = cherrypy.request.private_params["_viewId"]
return "extension.%s.socket.%s" % (view_id, topic)
class Server(object):
__default_server_config = {
......@@ -207,15 +206,6 @@ class Server(object):
self.__init_platform(**kwargs)
self.__init_plugins(**kwargs)
def __config_files(self, dir):
conf_d_dir = vispa.configpath(dir)
if os.path.isdir(conf_d_dir):
config_files = [f for f in os.listdir(conf_d_dir) if f.endswith(".ini") and os.path.isfile(os.path.join(conf_d_dir, f))]
config_files.sort()
return [os.path.join(conf_d_dir, f) for f in config_files]
else:
return []
def __init_paths(self, **kwargs):
# dir for variable files and folders
self.var_dir = os.path.abspath(kwargs.get('vardir', ''))
......@@ -245,47 +235,14 @@ class Server(object):
if not os.path.exists(self.cache_dir):
os.mkdir(self.cache_dir)
# conf dir
self.conf_dir = os.path.abspath(kwargs.get('configdir', ''))
logger.info('Using %s as config dir.' % self.conf_dir)
vispa.set_configpath(self.conf_dir)
vispa.setup_config(self.conf_dir)
logger.info('Read config file: %s', vispa.configpath('vispa.ini'))
vispa.config.read(vispa.configpath('vispa.ini'))
def __init_database(self, **kwargs):
# load all ini files
config_files = self.__config_files('vispa.d')
logger.info('Read config files: %s', config_files)
vispa.config.read(config_files)
self._engine = vispa.models.open_database()
def __init_database(self, **kwargs):
sa_identifier = vispa.config('database', 'sqlalchemy.url',
'sqlite:///%s/vispa.db' % self.var_dir)
pool_size = vispa.config('database', 'sqlalchemy.pool_size', 10)
max_overflow = vispa.config(
'database',
'sqlalchemy.max_overflow',
10)
# https://github.com/mitsuhiko/flask-sqlalchemy/issues/2
# http://docs.sqlalchemy.org/en/latest/core/pooling.html#dealing-with-disconnects
pool_recycle = vispa.config(
'database',
'sqlalchemy.pool_recycle',
7200)
logger.info('Use database %s.' % sa_identifier)
try:
self._engine = sqlalchemy.create_engine(
sa_identifier,
echo=False,
pool_size=pool_size,
pool_recycle=pool_recycle,
max_overflow=max_overflow)
except TypeError:
self._engine = sqlalchemy.create_engine(
sa_identifier,
echo=False)
if vispa.config('alembic', 'use_alembic', True) and not sa_identifier.startswith('sqlite'):
if vispa.config('alembic', 'use_alembic', False):
logger.info("Use alembic")
vispa.models.alembic.migrate(self._engine)
else:
......@@ -420,7 +377,7 @@ class Server(object):
logger.info('Merge cherrpy config file: %s' % cherrypy_conf)
cherrypy.config.update(cherrypy_conf)
config_files = self.__config_files("cherrypy.d")
config_files = vispa.config_files("cherrypy.d")
logger.info('Merge cherrypy config files: %s' % config_files)
for f in config_files:
cherrypy.config.update(f)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment