| 1 | #!/usr/bin/env python |
|---|
| 2 | #-*- coding: utf-8 -*- |
|---|
| 3 | ############################################################################# |
|---|
| 4 | # idcentral plugin |
|---|
| 5 | # Copyright (C) by USLA Team <-> devel-team usla org ar 2010 |
|---|
| 6 | # |
|---|
| 7 | # new_project is free software: you can redistribute it and/or modify it |
|---|
| 8 | # under the terms of the GNU General Public License as published by the |
|---|
| 9 | # Free Software Foundation, either version 3 of the License, or |
|---|
| 10 | # (at your option) any later version. |
|---|
| 11 | # |
|---|
| 12 | # idcentral.py is distributed in the hope that it will be useful, but |
|---|
| 13 | # WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|---|
| 15 | # See the GNU General Public License for more details. |
|---|
| 16 | # |
|---|
| 17 | # You should have received a copy of the GNU General Public License along |
|---|
| 18 | # with this program. If not, see <http://www.gnu.org/licenses/>. |
|---|
| 19 | ############################################################################# |
|---|
| 20 | |
|---|
| 21 | import re |
|---|
| 22 | import xmlrpclib |
|---|
| 23 | from hashlib import sha1 |
|---|
| 24 | from base64 import encodestring |
|---|
| 25 | |
|---|
| 26 | from trac.core import Component, implements |
|---|
| 27 | from trac.web import IRequestHandler |
|---|
| 28 | from trac.web.chrome import ITemplateProvider, add_stylesheet |
|---|
| 29 | from trac.config import Option |
|---|
| 30 | |
|---|
| 31 | class IdCentralPlugin(Component): |
|---|
| 32 | implements(IRequestHandler, ITemplateProvider) |
|---|
| 33 | |
|---|
| 34 | user_id_validator = re.compile("^[_\-\.a-zA-Z]+$") |
|---|
| 35 | sug_name_lastname = Option('IdCentral', 'sug_name_lastname', |
|---|
| 36 | doc=u'Sugerencias para el nombre') |
|---|
| 37 | sug_userid = Option('IdCentral', 'sug_project_id', |
|---|
| 38 | doc=u'Sugerencias para el Id del usuario') |
|---|
| 39 | sug_usergroup = Option('IdCentral', 'sug_usergroup', |
|---|
| 40 | doc=u'Sugerencia para el grupo de usuario') |
|---|
| 41 | sug_texcha = Option('IdCentral', 'sug_texcha', |
|---|
| 42 | doc=u'Sugerencia para el TexCha') |
|---|
| 43 | sug_passwd = Option('IdCentral', 'sug_passwd', |
|---|
| 44 | doc=u'Sugerencia para el Password') |
|---|
| 45 | sug_confirm_passwd = Option('IdCentral', 'sug_confirm_passwd', |
|---|
| 46 | doc=u'Sugerencia para el Confirm Password') |
|---|
| 47 | intro = Option('IdCentral', 'intro', |
|---|
| 48 | doc=u'Introducción del formulario de nuevo usuario') |
|---|
| 49 | daemon_idCentral = xmlrpclib.ServerProxy("http://localhost:8008", allow_none=1) |
|---|
| 50 | texcha = "" |
|---|
| 51 | |
|---|
| 52 | # IRequestHandler methods |
|---|
| 53 | def match_request(self, req): |
|---|
| 54 | return re.match(r'/(nuevo_usuario)(?:_trac)?(?:/.*)?$', req.path_info) |
|---|
| 55 | |
|---|
| 56 | def process_request(self, req): |
|---|
| 57 | add_stylesheet(req, 'nuldap/css/password.css') |
|---|
| 58 | action = req.args.get('action') |
|---|
| 59 | format = req.args.get('format') |
|---|
| 60 | path_info = req.path_info[1:] |
|---|
| 61 | self.data = {} |
|---|
| 62 | self.sugerencias() |
|---|
| 63 | self.data['orgs'] = self.daemon_idCentral.listORGs() |
|---|
| 64 | self.data['texcha'] = self.texcha |
|---|
| 65 | self.client = {} |
|---|
| 66 | self.client['ip'] = req.get_header('x-forwarded-for') |
|---|
| 67 | self.client['user-agent'] = req.get_header('user-agent') |
|---|
| 68 | if req.method == 'GET': |
|---|
| 69 | self.data['texcha'] = self.texcha = self.daemon_idCentral.getTexcha() |
|---|
| 70 | self.daemon_idCentral.log('DEBUG', u'[VIEW]: GET %r' % self.client) |
|---|
| 71 | elif req.method == 'POST' and action == 'newuser': |
|---|
| 72 | if self._parser(req): |
|---|
| 73 | self.daemon_idCentral.log('DEBUG', u'[VIEW]: POST %r Aceptados todos los chequeos' % self.client) |
|---|
| 74 | self.daemon_idCentral.bookUser(self.data['userid'], |
|---|
| 75 | self.data['name_lastname'], '{SHA}' + encodestring(sha1(self.data['passwd']).digest()), |
|---|
| 76 | self.data['lug_selected'], dict(req._inheaders)) |
|---|
| 77 | return 'usuario_creado.html', self.data, None |
|---|
| 78 | return 'nuevo_usuario.html', self.data, None |
|---|
| 79 | |
|---|
| 80 | def _parser(self, req): |
|---|
| 81 | u"""Método encargado de analizar las entradas del usuario y determinar |
|---|
| 82 | su validez. Le aclara al usuario lo que ha ingresado erróneamente""" |
|---|
| 83 | valid = True |
|---|
| 84 | self.data['name_lastname'] = req.args.get('name_lastname') |
|---|
| 85 | self.data['userid'] = req.args.get('userid').lower() |
|---|
| 86 | self.data['lug_selected'] = req.args.get('lug') |
|---|
| 87 | self.data[self.data['lug_selected']] = {"selected" : "True"} |
|---|
| 88 | self.data['orgs'] = self.daemon_idCentral.listORGs() |
|---|
| 89 | self.data['passwd'] = req.args.get('passwd') |
|---|
| 90 | self.data['confirm_passwd'] = req.args.get('confirm_passwd') |
|---|
| 91 | self.data['texcha_put'] = req.args.get('texcha_put') |
|---|
| 92 | if self.data['passwd'] != self.data['confirm_passwd']: |
|---|
| 93 | self.data['error_passwd'] = u'Las contraseñas no coinciden' |
|---|
| 94 | self.daemon_idCentral.log('DEBUG', u'[VIEW]: POST %r contraseñas no coinciden' % self.client) |
|---|
| 95 | valid = False |
|---|
| 96 | if not self.daemon_idCentral.validateTexcha(self.texcha, self.data['texcha_put']): |
|---|
| 97 | self.data['error_texcha'] = u'Tu respuesta al texto es incorrecta' |
|---|
| 98 | self.data['texcha'] = texcha = self.daemon_idCentral.getTexcha() |
|---|
| 99 | self.daemon_idCentral.log('DEBUG', u'[VIEW]: POST %r texcha inválido' % self.client) |
|---|
| 100 | valid = False |
|---|
| 101 | if not re.match(self.user_id_validator, self.data['userid']): |
|---|
| 102 | self.data['error_userid'] = u'Tu id Usuario posee carácteres no permitidos' |
|---|
| 103 | self.daemon_idCentral.log('DEBUG', u'[VIEW]: POST %r uid no permitido' % self.client) |
|---|
| 104 | valid = False |
|---|
| 105 | if len(self.data['name_lastname']) == 0: |
|---|
| 106 | self.data['error_name_lastname'] = u'El campo Usuario y Apellido es obligatorio' |
|---|
| 107 | self.daemon_idCentral.log('INFO', u'[VIEW]: POST %r El campo de nombre y Apellido no fue completado' % self.client) |
|---|
| 108 | valid = False |
|---|
| 109 | if self.daemon_idCentral.checkIfUserExist(self.data['userid']): |
|---|
| 110 | self.data['error_userid'] = u'Este usuario no está disponible, por favor elige otro' |
|---|
| 111 | self.daemon_idCentral.log('DEBUG', u'[VIEW]: POST %r usuario ya existente' % self.client) |
|---|
| 112 | valid = False |
|---|
| 113 | if len(self.data['passwd']) == 0 and valid: |
|---|
| 114 | self.data['passwd'] = self.daemon_idCentral.generateRandomPassword() |
|---|
| 115 | self.data['passwd_generated'] = u'Tu password generado es %s' % self.data['passwd'] |
|---|
| 116 | self.daemon_idCentral.log('INFO', u'[VIEW]: POST %r el usuario no eligió pass, se le genera uno' % self.client) |
|---|
| 117 | if self.daemon_idCentral.checkPasswordStrengh(self.data['passwd']) <= 2 and valid: |
|---|
| 118 | self.data['error_passwd'] = u'Tu contraseña es demasiado débil' |
|---|
| 119 | self.daemon_idCentral.log('INFO', u'[VIEW]: POST %r password demasiado débil' % self.client) |
|---|
| 120 | valid = False |
|---|
| 121 | return valid |
|---|
| 122 | |
|---|
| 123 | def sugerencias(self): |
|---|
| 124 | self.data['intro'] = self.intro |
|---|
| 125 | self.data['sug_name_lastname'] = self.sug_name_lastname |
|---|
| 126 | self.data['sug_userid'] = self.sug_userid |
|---|
| 127 | self.data['sug_usergroup'] = self.sug_usergroup |
|---|
| 128 | self.data['sug_passwd'] = self.sug_passwd |
|---|
| 129 | self.data['sug_confirm_passwd'] = self.sug_confirm_passwd |
|---|
| 130 | self.data['sug_texcha'] = self.sug_texcha |
|---|
| 131 | |
|---|
| 132 | # ITemplateProvider methods |
|---|
| 133 | # Used to add the plugin's templates and htdocs |
|---|
| 134 | def get_templates_dirs(self): |
|---|
| 135 | from pkg_resources import resource_filename |
|---|
| 136 | return [resource_filename(__name__, 'templates')] |
|---|
| 137 | |
|---|
| 138 | def get_htdocs_dirs(self): |
|---|
| 139 | """Return a list of directories with static resources (such as style |
|---|
| 140 | sheets, images, etc.) |
|---|
| 141 | |
|---|
| 142 | Each item in the list must be a `(prefix, abspath)` tuple. The |
|---|
| 143 | `prefix` part defines the path in the URL that requests to these |
|---|
| 144 | resources are prefixed with. |
|---|
| 145 | |
|---|
| 146 | The `abspath` is the absolute path to the directory containing the |
|---|
| 147 | resources on the local file system. |
|---|
| 148 | """ |
|---|
| 149 | from pkg_resources import resource_filename |
|---|
| 150 | return [('common', resource_filename('trac', 'htdocs')), |
|---|
| 151 | ('nuldap', resource_filename(__name__, 'htdocs'))] |
|---|