@@ -18,30 +18,31 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# MA 02110-1301, USA.
"""
Created on April 4, 2010
users controller for pylons
@author: marcink
from formencode import htmlfill
from pylons import request, session, tmpl_context as c, url
from pylons.controllers.util import abort, redirect
from pylons.i18n.translation import _
from rhodecode.lib.exceptions import *
from rhodecode.lib import helpers as h
from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
from rhodecode.lib.base import BaseController, render
from rhodecode.model.db import User, UserLog
from rhodecode.model.db import User
from rhodecode.model.forms import UserForm
from rhodecode.model.user import UserModel, DefaultUserException
from rhodecode.model.user import UserModel
import formencode
import logging
import traceback
log = logging.getLogger(__name__)
class UsersController(BaseController):
"""REST Controller styled on the Atom Publishing Protocol"""
# To properly map this controller, ensure your config/routing.py
# file has a resource setup:
# map.resource('user', 'users')
@@ -126,25 +127,25 @@ class UsersController(BaseController):
def delete(self, id):
"""DELETE /users/id: Delete an existing item"""
# Forms posted to this method should contain a hidden field:
# <input type="hidden" name="_method" value="DELETE" />
# Or using helpers:
# h.form(url('user', id=ID),
# method='delete')
# url('user', id=ID)
user_model = UserModel()
try:
user_model.delete(id)
h.flash(_('sucessfully deleted user'), category='success')
except DefaultUserException, e:
except (UserOwnsReposException, DefaultUserException), e:
h.flash(str(e), category='warning')
except Exception:
h.flash(_('An error occured during deletion of user'),
category='error')
return redirect(url('users'))
def show(self, id, format='html'):
"""GET /users/id: Show a specific item"""
def edit(self, id, format='html'):
@@ -15,26 +15,27 @@
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
from pylons import config, session, url, request
from rhodecode.lib.utils import get_repo_slug
from rhodecode.lib.auth_ldap import AuthLdap, UsernameError, PasswordError
from rhodecode.lib.auth_ldap import AuthLdap
from rhodecode.model import meta
from rhodecode.model.caching_query import FromCache
from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
UserToPerm
import bcrypt
from decorator import decorator
import random
@@ -120,25 +121,25 @@ def authfunc(environ, username, password
}
log.debug('Checking for ldap authentication')
aldap = AuthLdap(**kwargs)
res = aldap.authenticate_ldap(username, password)
authenticated = res[1]['uid'][0] == username
if authenticated and user_model.create_ldap(username, password):
log.info('created new ldap user')
return authenticated
except (UsernameError, PasswordError):
except (LdapUsernameError, LdapPasswordError):
return False
except:
log.error(traceback.format_exc())
class AuthUser(object):
A simple object that handles a mercurial username for authentication
def __init__(self):
self.username = 'None'
#==============================================================================
# LDAP
#Name = Just a description for the auth modes page
#Host = DepartmentName.OrganizationName.local/ IP
#Port = 389 default for ldap
#LDAPS = no set True if You need to use ldaps
#Account = DepartmentName\UserName (or UserName@MyDomain depending on AD server)
#Password = <password>
#Base DN = DC=DepartmentName,DC=OrganizationName,DC=local
#!/usr/bin/env python
# encoding: utf-8
# ldap authentication lib
# Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; version 2
# of the License or (at your opinion) any later version of the license.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
Created on Nov 17, 2010
from rhodecode.lib.exceptions import LdapImportError, UsernameError, \
PasswordError, ConnectionError
import ldap
except ImportError:
pass
class AuthLdap(object):
def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
@@ -52,42 +64,42 @@ class AuthLdap(object):
EnvironmentError if the LDAP server can't be reached.
:param username: username
:param password: password
from rhodecode.lib.helpers import chop_at
uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
dn = self.AUTH_DN % (uid, self.BASE_DN)
log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
if "," in username:
raise UsernameError("invalid character in username: ,")
raise LdapUsernameError("invalid character in username: ,")
ldap.set_option(ldap.OPT_X_TLS_CACERTFILE, '/etc/openldap/cacerts')
ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
server = ldap.initialize(self.LDAP_SERVER)
if self.ldap_version == 2:
server.protocol = ldap.VERSION2
else:
server.protocol = ldap.VERSION3
if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
server.simple_bind_s(self.AUTH_DN % (self.LDAP_BIND_DN,
self.BASE_DN),
self.LDAP_BIND_PASS)
server.simple_bind_s(dn, password)
properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
if not properties:
raise ldap.NO_SUCH_OBJECT()
except ldap.NO_SUCH_OBJECT, e:
log.debug("LDAP says no such user '%s' (%s)", uid, username)
raise UsernameError()
raise LdapUsernameError()
except ldap.INVALID_CREDENTIALS, e:
log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
raise PasswordError()
raise LdapPasswordError()
except ldap.SERVER_DOWN, e:
raise ConnectionError("LDAP can't access authentication server")
raise LdapConnectionError("LDAP can't access authentication server")
return properties[0]
@@ -14,16 +14,19 @@
Custom Exceptions modules
class UsernameError(Exception):pass
class PasswordError(Exception):pass
class ConnectionError(Exception):pass
class LdapUsernameError(Exception):pass
class LdapPasswordError(Exception):pass
class LdapConnectionError(Exception):pass
class LdapImportError(Exception):pass
class DefaultUserException(Exception):pass
class UserOwnsReposException(Exception):pass
@@ -39,24 +39,26 @@ class User(Base):
password = Column("password", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
active = Column("active", BOOLEAN(), nullable=True, unique=None, default=None)
admin = Column("admin", BOOLEAN(), nullable=True, unique=None, default=False)
name = Column("name", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
lastname = Column("lastname", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
email = Column("email", TEXT(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
last_login = Column("last_login", DATETIME(timezone=False), nullable=True, unique=None, default=None)
is_ldap = Column("is_ldap", BOOLEAN(), nullable=False, unique=None, default=False)
user_log = relation('UserLog', cascade='all')
user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
repositories = relation('Repository')
@LazyProperty
def full_contact(self):
return '%s %s <%s>' % (self.name, self.lastname, self.email)
def __repr__(self):
return "<User('id:%s:%s')>" % (self.user_id, self.username)
def update_lastlogin(self):
"""Update user lastlogin"""
import datetime
Created on April 9, 2010
Model for users
:author: marcink
from rhodecode.model.meta import Session
class UserModel(object):
self.sa = Session()
def get(self, user_id, cache=False):
user = self.sa.query(User)
if cache:
user = user.options(FromCache("sql_cache_short",
"get_user_%s" % user_id))
return user.get(user_id)
@@ -119,24 +120,25 @@ class UserModel(object):
self.sa.rollback()
raise
def update(self, user_id, form_data):
new_user = self.get(user_id, cache=False)
if new_user.username == 'default':
raise DefaultUserException(
_("You can't Edit this user since it's"
" crucial for entire application"))
for k, v in form_data.items():
if k == 'new_password' and v != '':
new_user.password = v
setattr(new_user, k, v)
self.sa.add(new_user)
self.sa.commit()
@@ -160,24 +162,30 @@ class UserModel(object):
def delete(self, user_id):
user = self.get(user_id, cache=False)
if user.username == 'default':
_("You can't remove this user since it's"
if user.repositories:
raise UserOwnsReposException(_('This user still owns %s '
'repositories and cannot be '
'removed. Switch owners or '
'remove those repositories') \
% user.repositories)
self.sa.delete(user)
def reset_password(self, data):
from rhodecode.lib.celerylib import tasks, run_task
run_task(tasks.reset_user_password, data['email'])
Status change: