changeset 4074:3b136af34329

Added pre-create user hook. It allows to control user creation using rcext hooks.
author Marcin Kuzminski <marcin@python-works.com>
date Mon, 01 Jul 2013 16:10:22 +0200
parents 2c82dd8ba318
children 92da990f9eaf
files rhodecode/config/rcextensions/__init__.py rhodecode/controllers/admin/users.py rhodecode/controllers/login.py rhodecode/lib/base.py rhodecode/lib/exceptions.py rhodecode/lib/hooks.py rhodecode/model/user.py rhodecode/templates/register.html
diffstat 8 files changed, 137 insertions(+), 24 deletions(-) [+]
line wrap: on
line diff
--- a/rhodecode/config/rcextensions/__init__.py	Mon Jul 01 15:39:01 2013 +0200
+++ b/rhodecode/config/rcextensions/__init__.py	Mon Jul 01 16:10:22 2013 +0200
@@ -53,6 +53,29 @@
 
 
 #==============================================================================
+# PRE CREATE USER HOOK
+#==============================================================================
+# this function will be executed before each user is created
+def _pre_cruserhook(*args, **kwargs):
+    """
+    Pre create user HOOK, it returns a tuple of bool, reason.
+    If bool is False the user creation will be stopped and reason
+    will be displayed to the user.
+    kwargs available:
+    :param username:
+    :param password:
+    :param email:
+    :param firstname:
+    :param lastname:
+    :param active:
+    :param admin:
+    :param created_by:
+    """
+    reason = 'allowed'
+    return True, reason
+PRE_CREATE_USER_HOOK = _pre_cruserhook
+
+#==============================================================================
 # POST CREATE USER HOOK
 #==============================================================================
 # this function will be executed after each user is created
--- a/rhodecode/controllers/admin/users.py	Mon Jul 01 15:39:01 2013 +0200
+++ b/rhodecode/controllers/admin/users.py	Mon Jul 01 16:10:22 2013 +0200
@@ -35,7 +35,7 @@
 
 import rhodecode
 from rhodecode.lib.exceptions import DefaultUserException, \
-    UserOwnsReposException
+    UserOwnsReposException, UserCreationError
 from rhodecode.lib import helpers as h
 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
     AuthUser
@@ -137,6 +137,8 @@
                 errors=errors.error_dict or {},
                 prefix_error=False,
                 encoding="UTF-8")
+        except UserCreationError, e:
+            h.flash(e, 'error')
         except Exception:
             log.error(traceback.format_exc())
             h.flash(_('Error occurred during creation of user %s') \
--- a/rhodecode/controllers/login.py	Mon Jul 01 15:39:01 2013 +0200
+++ b/rhodecode/controllers/login.py	Mon Jul 01 16:10:22 2013 +0200
@@ -37,6 +37,7 @@
 import rhodecode.lib.helpers as h
 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
 from rhodecode.lib.base import BaseController, render
+from rhodecode.lib.exceptions import UserCreationError
 from rhodecode.model.db import User
 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
 from rhodecode.model.user import UserModel
@@ -120,6 +121,12 @@
                     errors=errors.error_dict or {},
                     prefix_error=False,
                     encoding="UTF-8")
+            except UserCreationError, e:
+                # container auth or other auth functions that create users on
+                # the fly can throw this exception signaling that there's issue
+                # with user creation, explanation should be provided in
+                # Exception itself
+                h.flash(e, 'error')
 
         return render('/login.html')
 
@@ -147,6 +154,12 @@
                     errors=errors.error_dict or {},
                     prefix_error=False,
                     encoding="UTF-8")
+            except UserCreationError, e:
+                # container auth or other auth functions that create users on
+                # the fly can throw this exception signaling that there's issue
+                # with user creation, explanation should be provided in
+                # Exception itself
+                h.flash(e, 'error')
 
         return render('/register.html')
 
--- a/rhodecode/lib/base.py	Mon Jul 01 15:39:01 2013 +0200
+++ b/rhodecode/lib/base.py	Mon Jul 01 16:10:22 2013 +0200
@@ -22,6 +22,7 @@
 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
     HasPermissionAnyMiddleware, CookieStoreWrapper
 from rhodecode.lib.utils import get_repo_slug
+from rhodecode.lib.exceptions import UserCreationError
 from rhodecode.model import meta
 
 from rhodecode.model.db import Repository, RhodeCodeUi, User, RhodeCodeSetting
@@ -300,7 +301,17 @@
             cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
             user_id = cookie_store.get('user_id', None)
             username = get_container_username(environ, config)
-            auth_user = AuthUser(user_id, api_key, username, self.ip_addr)
+            try:
+                auth_user = AuthUser(user_id, api_key, username, self.ip_addr)
+            except UserCreationError, e:
+                from rhodecode.lib import helpers as h
+                h.flash(e, 'error')
+                # container auth or other auth functions that create users on
+                # the fly can throw this exception signaling that there's issue
+                # with user creation, explanation should be provided in
+                # Exception itself
+                auth_user = AuthUser(ip_addr=self.ip_addr)
+
             request.user = auth_user
             self.rhodecode_user = c.rhodecode_user = auth_user
             if not self.rhodecode_user.is_authenticated and \
--- a/rhodecode/lib/exceptions.py	Mon Jul 01 15:39:01 2013 +0200
+++ b/rhodecode/lib/exceptions.py	Mon Jul 01 16:10:22 2013 +0200
@@ -90,3 +90,11 @@
 
 class IMCCommitError(Exception):
     pass
+
+
+class UserCreationError(Exception):
+    pass
+
+
+class RepositoryCreationError(Exception):
+    pass
--- a/rhodecode/lib/hooks.py	Mon Jul 01 15:39:01 2013 +0200
+++ b/rhodecode/lib/hooks.py	Mon Jul 01 16:10:22 2013 +0200
@@ -34,7 +34,7 @@
 from rhodecode.lib.utils import action_logger
 from rhodecode.lib.vcs.backends.base import EmptyChangeset
 from rhodecode.lib.compat import json
-from rhodecode.lib.exceptions import HTTPLockedRC
+from rhodecode.lib.exceptions import HTTPLockedRC, UserCreationError
 from rhodecode.lib.utils2 import safe_str, _extract_extras
 from rhodecode.model.db import Repository, User
 
@@ -252,6 +252,15 @@
     return 0
 
 
+def check_allowed_create_user(user_dict, created_by, **kwargs):
+    from rhodecode import EXTENSIONS
+    callback = getattr(EXTENSIONS, 'PRE_CREATE_USER_HOOK', None)
+    if isfunction(callback):
+        allowed, reason = callback(created_by=created_by, **user_dict)
+        if not allowed:
+            raise UserCreationError(reason)
+
+
 def log_create_user(user_dict, created_by, **kwargs):
     """
     Post create user Hook. This is a dummy function for admins to re-use
--- a/rhodecode/model/user.py	Mon Jul 01 15:39:01 2013 +0200
+++ b/rhodecode/model/user.py	Mon Jul 01 16:10:22 2013 +0200
@@ -36,9 +36,9 @@
 from rhodecode.lib.utils2 import safe_unicode, generate_api_key, get_current_rhodecode_user
 from rhodecode.lib.caching_query import FromCache
 from rhodecode.model import BaseModel
-from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
+from rhodecode.model.db import User, Repository, Permission, \
     UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
-    Notification, RepoGroup, UserRepoGroupToPerm, UserGroupRepoGroupToPerm, \
+    Notification, RepoGroup, UserGroupRepoGroupToPerm, \
     UserEmailMap, UserIpMap, UserGroupUserGroupToPerm, UserGroup
 from rhodecode.lib.exceptions import DefaultUserException, \
     UserOwnsReposException
@@ -83,6 +83,17 @@
     def create(self, form_data, cur_user=None):
         if not cur_user:
             cur_user = getattr(get_current_rhodecode_user(), 'username', None)
+
+        from rhodecode.lib.hooks import log_create_user, check_allowed_create_user
+        _fd = form_data
+        form_data = {
+            'username': _fd['username'], 'password': _fd['password'],
+            'email': _fd['email'], 'firstname': _fd['firstname'], 'lastname': _fd['lastname'],
+            'active': _fd['active'], 'admin': False
+        }
+        # raises UserCreationError if it's not allowed
+        check_allowed_create_user(form_data, cur_user)
+
         from rhodecode.lib.auth import get_crypt_password
         try:
             new_user = User()
@@ -96,7 +107,6 @@
             new_user.api_key = generate_api_key(form_data['username'])
             self.sa.add(new_user)
 
-            from rhodecode.lib.hooks import log_create_user
             log_create_user(new_user.get_dict(), cur_user)
             return new_user
         except Exception:
@@ -124,6 +134,14 @@
             cur_user = getattr(get_current_rhodecode_user(), 'username', None)
 
         from rhodecode.lib.auth import get_crypt_password
+        from rhodecode.lib.hooks import log_create_user, check_allowed_create_user
+        form_data = {
+            'username': username, 'password': password,
+            'email': email, 'firstname': firstname, 'lastname': lastname,
+            'active': active, 'admin': admin
+        }
+        # raises UserCreationError if it's not allowed
+        check_allowed_create_user(form_data, cur_user)
 
         log.debug('Checking for %s account in RhodeCode database' % username)
         user = User.get_by_username(username, case_insensitive=True)
@@ -151,7 +169,6 @@
             self.sa.add(new_user)
 
             if not edit:
-                from rhodecode.lib.hooks import log_create_user
                 log_create_user(new_user.get_dict(), cur_user)
             return new_user
         except (DatabaseError,):
@@ -169,23 +186,33 @@
         if not cur_user:
             cur_user = getattr(get_current_rhodecode_user(), 'username', None)
         if self.get_by_username(username, case_insensitive=True) is None:
-
             # autogenerate email for container account without one
             generate_email = lambda usr: '%s@container_auth.account' % usr
+            firstname = attrs['name']
+            lastname = attrs['lastname']
+            active = attrs.get('active', True)
+            email = attrs['email'] or generate_email(username)
+
+            from rhodecode.lib.hooks import log_create_user, check_allowed_create_user
+            form_data = {
+                'username': username, 'password': None,
+                'email': email, 'firstname': firstname, 'lastname': lastname,
+                'active': attrs.get('active', True), 'admin': False
+            }
+            # raises UserCreationError if it's not allowed
+            check_allowed_create_user(form_data, cur_user)
 
             try:
                 new_user = User()
                 new_user.username = username
                 new_user.password = None
                 new_user.api_key = generate_api_key(username)
-                new_user.email = attrs['email']
-                new_user.active = attrs.get('active', True)
-                new_user.name = attrs['name'] or generate_email(username)
-                new_user.lastname = attrs['lastname']
+                new_user.email = email
+                new_user.active = active
+                new_user.name = firstname
+                new_user.lastname = lastname
 
                 self.sa.add(new_user)
-
-                from rhodecode.lib.hooks import log_create_user
                 log_create_user(new_user.get_dict(), cur_user)
                 return new_user
             except (DatabaseError,):
@@ -212,26 +239,37 @@
         from rhodecode.lib.auth import get_crypt_password
         log.debug('Checking for such ldap account in RhodeCode database')
         if self.get_by_username(username, case_insensitive=True) is None:
+            # autogenerate email for container account without one
+            generate_email = lambda usr: '%s@ldap.account' % usr
+            password = get_crypt_password(password)
+            firstname = attrs['name']
+            lastname = attrs['lastname']
+            active = attrs.get('active', True)
+            email = attrs['email'] or generate_email(username)
 
-            # autogenerate email for ldap account without one
-            generate_email = lambda usr: '%s@ldap.account' % usr
+            from rhodecode.lib.hooks import log_create_user, check_allowed_create_user
+            form_data = {
+                'username': username, 'password': password,
+                'email': email, 'firstname': firstname, 'lastname': lastname,
+                'active': attrs.get('active', True), 'admin': False
+            }
+            # raises UserCreationError if it's not allowed
+            check_allowed_create_user(form_data, cur_user)
 
             try:
                 new_user = User()
                 username = username.lower()
                 # add ldap account always lowercase
                 new_user.username = username
-                new_user.password = get_crypt_password(password)
+                new_user.password = password
                 new_user.api_key = generate_api_key(username)
-                new_user.email = attrs['email'] or generate_email(username)
-                new_user.active = attrs.get('active', True)
+                new_user.email = email
+                new_user.active = active
                 new_user.ldap_dn = safe_unicode(user_dn)
-                new_user.name = attrs['name']
-                new_user.lastname = attrs['lastname']
-
+                new_user.name = firstname
+                new_user.lastname = lastname
                 self.sa.add(new_user)
 
-                from rhodecode.lib.hooks import log_create_user
                 log_create_user(new_user.get_dict(), cur_user)
                 return new_user
             except (DatabaseError,):
--- a/rhodecode/templates/register.html	Mon Jul 01 15:39:01 2013 +0200
+++ b/rhodecode/templates/register.html	Mon Jul 01 16:10:22 2013 +0200
@@ -6,7 +6,16 @@
 </%def>
 
 <div id="register">
-
+<div class="flash_msg">
+    <% messages = h.flash.pop_messages() %>
+    % if messages:
+    <ul id="flash-messages">
+        % for message in messages:
+        <li class="${message.category}_msg">${message}</li>
+        % endfor
+    </ul>
+    % endif
+</div>
     <div class="title top-left-rounded-corner top-right-rounded-corner">
         <h5>${_('Sign Up to')} ${c.rhodecode_name}</h5>
     </div>