mirror of
https://github.com/YunoHost/moulinette.git
synced 2024-09-03 20:06:31 +02:00
Exception handling
This commit is contained in:
parent
5dbce99e65
commit
4e1dd30c4c
6 changed files with 223 additions and 191 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -4,6 +4,7 @@
|
||||||
*.egg
|
*.egg
|
||||||
*.egg-info
|
*.egg-info
|
||||||
*.swp
|
*.swp
|
||||||
|
*.swo
|
||||||
dist
|
dist
|
||||||
build
|
build
|
||||||
eggs
|
eggs
|
||||||
|
|
149
lib/yunohost.py
Normal file
149
lib/yunohost.py
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import ldap
|
||||||
|
import ldap.modlist as modlist
|
||||||
|
import re
|
||||||
|
import getpass
|
||||||
|
|
||||||
|
|
||||||
|
def colorize(astr, color):
|
||||||
|
color_dict = {
|
||||||
|
'red' : '31',
|
||||||
|
'green' : '32',
|
||||||
|
'yellow': '33',
|
||||||
|
'cyan' : '34',
|
||||||
|
'purple': '35'
|
||||||
|
}
|
||||||
|
return "\033["+ color_dict[color] +"m\033[1m" + astr + "\033[m"
|
||||||
|
|
||||||
|
def win_msg(astr):
|
||||||
|
if os.isatty(1):
|
||||||
|
print('\n' + colorize(_("Success: "), 'green') + astr + '\n')
|
||||||
|
|
||||||
|
def str_to_func(astr):
|
||||||
|
"""
|
||||||
|
Call a function from a string name
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
astr -- Name of function to call
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Function
|
||||||
|
|
||||||
|
"""
|
||||||
|
module, _, function = astr.rpartition('.')
|
||||||
|
if module:
|
||||||
|
__import__(module)
|
||||||
|
mod = sys.modules[module]
|
||||||
|
else:
|
||||||
|
mod = sys.modules['__main__'] # default module
|
||||||
|
|
||||||
|
try:
|
||||||
|
func = getattr(mod, function)
|
||||||
|
except NameError:
|
||||||
|
raise YunoHostError(168, _('Function is not defined'))
|
||||||
|
else:
|
||||||
|
return func
|
||||||
|
|
||||||
|
|
||||||
|
class YunoHostError(Exception):
|
||||||
|
""" Custom exception """
|
||||||
|
def __init__(self, code, message):
|
||||||
|
code_dict = {
|
||||||
|
1 : _('Fail'),
|
||||||
|
13 : _('Permission denied'),
|
||||||
|
17 : _('Already exists'),
|
||||||
|
22 : _('Invalid arguments'),
|
||||||
|
87 : _('Too many users'),
|
||||||
|
111 : _('Connection refused'),
|
||||||
|
122 : _('Quota exceeded'),
|
||||||
|
125 : _('Operation canceled'),
|
||||||
|
167 : _('Not found'),
|
||||||
|
168 : _('Undefined'),
|
||||||
|
169 : _('LDAP operation error')
|
||||||
|
}
|
||||||
|
self.code = code
|
||||||
|
self.message = message
|
||||||
|
if code_dict[code]:
|
||||||
|
self.desc = code_dict[code]
|
||||||
|
else:
|
||||||
|
self.desc = code
|
||||||
|
|
||||||
|
|
||||||
|
class YunoHostLDAP:
|
||||||
|
""" Specific LDAP functions for YunoHost """
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
""" Connect to LDAP base """
|
||||||
|
|
||||||
|
self.conn = ldap.initialize('ldap://localhost:389')
|
||||||
|
self.base = 'dc=yunohost,dc=org'
|
||||||
|
self.pwd = getpass.getpass(_('LDAP Admin Password: '))
|
||||||
|
try:
|
||||||
|
self.conn.simple_bind_s('cn=admin,' + self.base, self.pwd)
|
||||||
|
except ldap.INVALID_CREDENTIALS:
|
||||||
|
raise YunoHostError(13, _('Invalid credentials'))
|
||||||
|
|
||||||
|
def disconnect(self):
|
||||||
|
""" Unbind from LDAP """
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.conn.unbind_s()
|
||||||
|
except:
|
||||||
|
raise YunoHostError(169, _('An error occured during disconnection'))
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
def search(self, base=None, filter='(objectClass=*)', attrs=['dn']):
|
||||||
|
""" Search in LDAP base """
|
||||||
|
|
||||||
|
if not base:
|
||||||
|
base = self.base
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = self.conn.search_s(base, ldap.SCOPE_SUBTREE, filter, attrs)
|
||||||
|
except:
|
||||||
|
raise YunoHostError(169, _('An error occured during LDAP search'))
|
||||||
|
|
||||||
|
if result:
|
||||||
|
result_list = []
|
||||||
|
for dn, entry in result:
|
||||||
|
if 'dn' in attrs:
|
||||||
|
entry['dn'] = [dn]
|
||||||
|
result_list.append(entry)
|
||||||
|
return result_list
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def add(self, rdn, attr_dict):
|
||||||
|
""" Add LDAP entry """
|
||||||
|
|
||||||
|
dn = rdn + ',' + self.base
|
||||||
|
ldif = modlist.addModlist(attr_dict)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.conn.add_s(dn, ldif)
|
||||||
|
except:
|
||||||
|
raise YunoHostError(169, _('An error occured during LDAP entry creation'))
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def validate(self, regex_dict):
|
||||||
|
for attr, pattern in regex_dict.items():
|
||||||
|
if re.match(pattern, attr):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise YunoHostError(22, _('Invalid attribute') + ' ' + attr)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def validate_uniqueness(self, value_dict):
|
||||||
|
for attr, value in value_dict.items():
|
||||||
|
if not self.search(filter=attr + '=' + value):
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
raise YunoHostError(17, _('Attribute already exists') + ' "' + attr + '=' + value + '"')
|
||||||
|
return True
|
||||||
|
|
|
@ -1,90 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import sys
|
|
||||||
import ldap
|
|
||||||
import ldap.modlist as modlist
|
|
||||||
import re
|
|
||||||
import getpass
|
|
||||||
import yunohost_messages as msg
|
|
||||||
|
|
||||||
class YunoHostLDAP:
|
|
||||||
""" Specific LDAP functions for YunoHost """
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
""" Connect to LDAP base """
|
|
||||||
|
|
||||||
self.conn = ldap.initialize('ldap://localhost:389')
|
|
||||||
self.base = 'dc=yunohost,dc=org'
|
|
||||||
self.pwd = getpass.getpass(_('LDAP Admin Password: '))
|
|
||||||
try:
|
|
||||||
self.conn.simple_bind_s('cn=admin,' + self.base, self.pwd)
|
|
||||||
except ldap.INVALID_CREDENTIALS:
|
|
||||||
print(msg.error + _('Wrong credentials'))
|
|
||||||
sys.exit(msg.ECONNREFUSED)
|
|
||||||
|
|
||||||
def disconnect(self):
|
|
||||||
""" Unbind from LDAP """
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.conn.unbind_s()
|
|
||||||
except:
|
|
||||||
print(msg.error + _('A problem occured during LDAP unbind'))
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
def search(self, base=None, filter='(objectClass=*)', attrs=['dn']):
|
|
||||||
""" Search in LDAP base """
|
|
||||||
|
|
||||||
if not base:
|
|
||||||
base = self.base
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = self.conn.search_s(base, ldap.SCOPE_SUBTREE, filter, attrs)
|
|
||||||
except:
|
|
||||||
print(msg.error + _('An error occured during LDAP search'))
|
|
||||||
return False
|
|
||||||
|
|
||||||
if result:
|
|
||||||
result_list = []
|
|
||||||
for dn, entry in result:
|
|
||||||
if 'dn' in attrs:
|
|
||||||
entry['dn'] = [dn]
|
|
||||||
result_list.append(entry)
|
|
||||||
return result_list
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
|
|
||||||
def add(self, rdn, attr_dict):
|
|
||||||
""" Add LDAP entry """
|
|
||||||
|
|
||||||
dn = rdn + ',' + self.base
|
|
||||||
ldif = modlist.addModlist(attr_dict)
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.conn.add_s(dn, ldif)
|
|
||||||
except:
|
|
||||||
print(msg.error + _('An error occured during LDAP entry creation'))
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def validate(self, regex_dict):
|
|
||||||
for attr, pattern in regex_dict.items():
|
|
||||||
if re.match(pattern, attr):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
print(msg.error + _('Invalid value') + ' "' + attr + '"')
|
|
||||||
sys.exit(msg.EINVAL)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def validate_uniqueness(self, value_dict):
|
|
||||||
for attr, value in value_dict.items():
|
|
||||||
if not self.search(filter=attr + '=' + value):
|
|
||||||
continue
|
|
||||||
else:
|
|
||||||
print(msg.error + _('Attribute already exists') + ' "' + attr + '=' + value + '"')
|
|
||||||
sys.exit(msg.EEXIST)
|
|
||||||
return True
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
""" Colored status messages """
|
|
||||||
error = "\033[31m\033[1m" + _("Error:") + "\033[m " # Red
|
|
||||||
interrupt = "\033[31m\033[1m" + _("Interrupt:") + "\033[m " # Red
|
|
||||||
notice = "\033[34m\033[1m" + _("Notice:") + "\033[m " # Cyan
|
|
||||||
success = "\033[32m\033[1m" + _("Success:") + "\033[m " # Green
|
|
||||||
|
|
||||||
|
|
||||||
""" Error codes """
|
|
||||||
EACCES = 13 # Permission denied
|
|
||||||
EEXIST = 17 # Exists
|
|
||||||
EINVAL = 22 # Invalid argument
|
|
||||||
EUSERS = 87 # Too many users
|
|
||||||
ECONNREFUSED = 111 # Connection refused
|
|
||||||
EDQUOTA = 122 # Quota exceeded
|
|
||||||
ECANCELED = 125 # Operation Canceled
|
|
||||||
ENOTFOUND = 167 # Not found
|
|
||||||
EUNDEFINED = 168 # Undefined
|
|
||||||
ELDAP = 169 # LDAP operation error
|
|
|
@ -1,16 +1,16 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import ldap
|
import ldap
|
||||||
import yunohost_ldap
|
|
||||||
import yunohost_messages as msg
|
|
||||||
import crypt
|
import crypt
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
import getpass
|
import getpass
|
||||||
|
from yunohost import YunoHostError, YunoHostLDAP, win_msg
|
||||||
|
|
||||||
# Initialize LDAP
|
# Initialize LDAP
|
||||||
yldap = yunohost_ldap.YunoHostLDAP()
|
yldap = YunoHostLDAP()
|
||||||
|
|
||||||
def user_list(args): # TODO : fix
|
def user_list(args): # TODO : fix
|
||||||
result = yldap.search()
|
result = yldap.search()
|
||||||
|
@ -33,17 +33,21 @@ def user_add(args):
|
||||||
try:
|
try:
|
||||||
for arg in required_args:
|
for arg in required_args:
|
||||||
if not args[arg]:
|
if not args[arg]:
|
||||||
|
if os.isatty(1):
|
||||||
args[arg] = raw_input(arg.capitalize()+': ')
|
args[arg] = raw_input(arg.capitalize()+': ')
|
||||||
|
else:
|
||||||
|
raise Exception
|
||||||
|
# Password
|
||||||
if not args['password']:
|
if not args['password']:
|
||||||
|
if os.isatty(1):
|
||||||
args['password'] = getpass.getpass()
|
args['password'] = getpass.getpass()
|
||||||
pwd2 = getpass.getpass('Retype password:')
|
pwd2 = getpass.getpass('Retype password:')
|
||||||
if args['password'] != pwd2:
|
if args['password'] != pwd2:
|
||||||
print(msg.error + _("Passwords doesn't match"))
|
raise YunoHostError(22, _("Passwords doesn't match"))
|
||||||
sys.exit(msg.EINVAL)
|
else:
|
||||||
|
raise YunoHostError(22, _("Missing arguments"))
|
||||||
except KeyboardInterrupt, EOFError:
|
except KeyboardInterrupt, EOFError:
|
||||||
print("\n" + msg.interrupt + _("User not created"))
|
raise YunoHostError(125, _("Interrupted, user not created"))
|
||||||
sys.exit(msg.ECANCELED)
|
|
||||||
|
|
||||||
# Manage values
|
# Manage values
|
||||||
fullname = args['firstname'] + ' ' + args['lastname']
|
fullname = args['firstname'] + ' ' + args['lastname']
|
||||||
|
@ -51,7 +55,7 @@ def user_add(args):
|
||||||
char_set = string.ascii_uppercase + string.digits
|
char_set = string.ascii_uppercase + string.digits
|
||||||
salt = ''.join(random.sample(char_set,8))
|
salt = ''.join(random.sample(char_set,8))
|
||||||
salt = '$1$' + salt + '$'
|
salt = '$1$' + salt + '$'
|
||||||
pwd = "{CRYPT}" + crypt.crypt(str(args['password']), salt)
|
pwd = '{CRYPT}' + crypt.crypt(str(args['password']), salt)
|
||||||
attr_dict = {
|
attr_dict = {
|
||||||
'objectClass' : ['mailAccount', 'inetOrgPerson'],
|
'objectClass' : ['mailAccount', 'inetOrgPerson'],
|
||||||
'givenName' : args['firstname'],
|
'givenName' : args['firstname'],
|
||||||
|
@ -77,11 +81,7 @@ def user_add(args):
|
||||||
})
|
})
|
||||||
|
|
||||||
if yldap.add(rdn, attr_dict):
|
if yldap.add(rdn, attr_dict):
|
||||||
print('\n ' + msg.success + _('User successfully created') + '\n')
|
win_msg(_("User successfully created"))
|
||||||
for attr, value in attr_dict.items():
|
return attr_dict
|
||||||
if attr != 'objectClass':
|
|
||||||
print('\033[35m\033[1m ' + attr + ': \033[m' + value)
|
|
||||||
return True
|
|
||||||
else:
|
else:
|
||||||
print(msg.error + _('An error occured during user creation'))
|
raise YunoHostError(169, _('An error occured during user creation'))
|
||||||
return False
|
|
||||||
|
|
108
yunohost
108
yunohost
|
@ -20,39 +20,44 @@ __credits__ = """
|
||||||
__author__ = 'Kload <kload@kload.fr>'
|
__author__ = 'Kload <kload@kload.fr>'
|
||||||
__version__ = '2.0 beta1'
|
__version__ = '2.0 beta1'
|
||||||
|
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
import argparse
|
import argparse
|
||||||
import gettext
|
import gettext
|
||||||
|
import json
|
||||||
|
if not __debug__:
|
||||||
|
import traceback
|
||||||
|
|
||||||
sys.path.append('lib') # Local temporary hack
|
sys.path.append('lib') # Local temporary hack
|
||||||
gettext.install('YunoHost')
|
gettext.install('YunoHost')
|
||||||
import yunohost_messages as msg
|
|
||||||
|
|
||||||
def str_to_func(astr):
|
from yunohost import YunoHostError, str_to_func, colorize
|
||||||
"""
|
|
||||||
Call a function from a string name
|
|
||||||
|
|
||||||
Keyword arguments:
|
|
||||||
astr -- Name of function to call
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Function
|
|
||||||
|
|
||||||
"""
|
|
||||||
module, _, function = astr.rpartition('.')
|
|
||||||
if module:
|
|
||||||
__import__(module)
|
|
||||||
mod = sys.modules[module]
|
|
||||||
else:
|
|
||||||
mod = sys.modules['__main__'] # default module
|
|
||||||
|
|
||||||
try:
|
|
||||||
func = getattr(mod, function)
|
|
||||||
except NameError:
|
|
||||||
print(msg.error + _('Function is not defined'))
|
|
||||||
sys.exit(msg.EUNDEFINED)
|
|
||||||
else:
|
|
||||||
return func
|
|
||||||
|
|
||||||
|
action_dict = {
|
||||||
|
'user' : {
|
||||||
|
'help' : 'Manage users',
|
||||||
|
'actions' : {
|
||||||
|
'list' : 'List users',
|
||||||
|
'add' : 'Add user'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'domain' : {
|
||||||
|
'help' : 'Manage domains',
|
||||||
|
'actions' : {}
|
||||||
|
},
|
||||||
|
'app' : {
|
||||||
|
'help' : 'Manage apps',
|
||||||
|
'actions' : {}
|
||||||
|
},
|
||||||
|
'monitor' : {
|
||||||
|
'help' : 'Monitoring functions',
|
||||||
|
'actions' : {}
|
||||||
|
},
|
||||||
|
'tools' : {
|
||||||
|
'help' : 'Specific tools',
|
||||||
|
'actions' : {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def dict_to_parsers(action_dict):
|
def dict_to_parsers(action_dict):
|
||||||
"""
|
"""
|
||||||
|
@ -82,7 +87,6 @@ def dict_to_parsers(action_dict):
|
||||||
|
|
||||||
return parsers
|
return parsers
|
||||||
|
|
||||||
|
|
||||||
def parse_args(parsers):
|
def parse_args(parsers):
|
||||||
"""
|
"""
|
||||||
Add and parse arguments
|
Add and parse arguments
|
||||||
|
@ -136,40 +140,28 @@ def parse_args(parsers):
|
||||||
return args
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
""" Main instructions """
|
""" Main instructions """
|
||||||
|
try:
|
||||||
action_dict = {
|
|
||||||
'user' : {
|
|
||||||
'help' : 'Manage users',
|
|
||||||
'actions' : {
|
|
||||||
'list' : 'List users',
|
|
||||||
'add' : 'Add user'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'domain' : {
|
|
||||||
'help' : 'Manage domains',
|
|
||||||
'actions' : {}
|
|
||||||
},
|
|
||||||
'app' : {
|
|
||||||
'help' : 'Manage apps',
|
|
||||||
'actions' : {}
|
|
||||||
},
|
|
||||||
'monitor' : {
|
|
||||||
'help' : 'Monitoring functions',
|
|
||||||
'actions' : {}
|
|
||||||
},
|
|
||||||
'tools' : {
|
|
||||||
'help' : 'Specific tools',
|
|
||||||
'actions' : {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parsers = dict_to_parsers(action_dict)
|
parsers = dict_to_parsers(action_dict)
|
||||||
args = parse_args(parsers)
|
args = parse_args(parsers)
|
||||||
args.func(vars(args))
|
result = args.func(vars(args))
|
||||||
|
except YunoHostError, error:
|
||||||
|
if not __debug__ :
|
||||||
|
traceback.print_exc()
|
||||||
|
if os.isatty(1):
|
||||||
|
print('\n' + colorize(_('Error: '), 'red') + error.message)
|
||||||
|
else:
|
||||||
|
print(json.dumps({ 'error' : error.message }))
|
||||||
|
return error.code
|
||||||
|
else:
|
||||||
|
if os.isatty(1):
|
||||||
|
for attr, value in result.items():
|
||||||
|
if type(value) is str:
|
||||||
|
print(colorize(attr, 'purple') + ': ' + value)
|
||||||
|
else:
|
||||||
|
print(json.dumps(result))
|
||||||
|
return 0
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
sys.exit(main())
|
||||||
|
|
Loading…
Reference in a new issue