def validate_python(self, value, state): from kallithea.lib import auth_modules password = value['password'] username = value['username'] # authenticate returns unused dict but has called # plugin._authenticate which has create_or_update'ed the username user in db if auth_modules.authenticate(username, password) is None: user = User.get_by_username_or_email(username) if user and not user.active: log.warning('user %s is disabled', username) msg = self.message('invalid_auth', state) raise formencode.Invalid(msg, value, state, error_dict=dict(username='******', password=msg)) else: log.warning('user %s failed to authenticate', username) msg = self.message('invalid_auth', state) raise formencode.Invalid(msg, value, state, error_dict=dict(username='******', password=msg))
def index(self): c.came_from = request.GET.get('came_from', '') if c.came_from: if not self._validate_came_from(c.came_from): log.error('Invalid came_from (not server-relative): %r', c.came_from) raise HTTPBadRequest() else: c.came_from = url('home') if request.POST: # import Login Form validator class login_form = LoginForm()() try: # login_form will check username/password using ValidAuth and report failure to the user c.form_result = login_form.to_python(dict(request.POST)) username = c.form_result['username'] user = User.get_by_username_or_email(username) assert user is not None # the same user get just passed in the form validation except formencode.Invalid as errors: defaults = errors.value # remove password from filling in form again defaults.pop('password', None) return htmlfill.render(render('/login.html'), defaults=errors.value, errors=errors.error_dict or {}, prefix_error=False, encoding="UTF-8", force_defaults=False) except UserCreationError as 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') else: # login_form already validated the password - now set the session cookie accordingly auth_user = log_in_user(user, c.form_result['remember'], is_external_auth=False, ip_addr=request.ip_addr) if auth_user: raise HTTPFound(location=c.came_from) h.flash(_('Authentication failed.'), 'error') else: # redirect if already logged in if not request.authuser.is_anonymous: raise HTTPFound(location=c.came_from) # continue to show login to default user return render('/login.html')
def index(self): c.came_from = safe_str(request.GET.get('came_from', '')) if c.came_from: if not self._validate_came_from(c.came_from): log.error('Invalid came_from (not server-relative): %r', c.came_from) raise HTTPBadRequest() else: c.came_from = url('home') ip_allowed = AuthUser.check_ip_allowed(self.authuser, self.ip_addr) # redirect if already logged in if self.authuser.is_authenticated and ip_allowed: raise HTTPFound(location=c.came_from) if request.POST: # import Login Form validator class login_form = LoginForm() try: c.form_result = login_form.to_python(dict(request.POST)) # form checks for username/password, now we're authenticated username = c.form_result['username'] user = User.get_by_username_or_email(username, case_insensitive=True) except formencode.Invalid as errors: defaults = errors.value # remove password from filling in form again del defaults['password'] return htmlfill.render( render('/login.html'), defaults=errors.value, errors=errors.error_dict or {}, prefix_error=False, encoding="UTF-8", force_defaults=False) except UserCreationError as 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') else: log_in_user(user, c.form_result['remember'], is_external_auth=False) raise HTTPFound(location=c.came_from) return render('/login.html')
def index(self): c.came_from = safe_str(request.GET.get('came_from', '')) if c.came_from: if not self._validate_came_from(c.came_from): log.error('Invalid came_from (not server-relative): %r', c.came_from) raise HTTPBadRequest() else: c.came_from = url('home') ip_allowed = AuthUser.check_ip_allowed(request.authuser, request.ip_addr) # redirect if already logged in if request.authuser.is_authenticated and ip_allowed: raise HTTPFound(location=c.came_from) if request.POST: # import Login Form validator class login_form = LoginForm()() try: c.form_result = login_form.to_python(dict(request.POST)) # form checks for username/password, now we're authenticated username = c.form_result['username'] user = User.get_by_username_or_email(username, case_insensitive=True) except formencode.Invalid as errors: defaults = errors.value # remove password from filling in form again defaults.pop('password', None) return htmlfill.render( render('/login.html'), defaults=errors.value, errors=errors.error_dict or {}, prefix_error=False, encoding="UTF-8", force_defaults=False) except UserCreationError as 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') else: log_in_user(user, c.form_result['remember'], is_external_auth=False) raise HTTPFound(location=c.came_from) return render('/login.html')
def get_user(self, username=None, **kwargs): """ Helper method for user fetching in plugins, by default it's using simple fetch by username, but this method can be customized in plugins eg. container auth plugin to fetch user by environ params :param username: username if given to fetch from database :param kwargs: extra arguments needed for user fetching. """ user = None log.debug('Trying to fetch user `%s` from Kallithea database', username) if username: user = User.get_by_username_or_email(username) else: log.debug('provided username:`%s` is empty skipping...', username) return user
def get_user(self, username=None, **kwargs): """ Helper method for user fetching in plugins, by default it's using simple fetch by username, but this method can be customized in plugins eg. container auth plugin to fetch user by environ params :param username: username if given to fetch from database :param kwargs: extra arguments needed for user fetching. """ user = None log.debug('Trying to fetch user `%s` from Kallithea database', username) if username: user = User.get_by_username_or_email(username) if user is None: log.debug('Fallback to fetch user in case insensitive mode') user = User.get_by_username(username, case_insensitive=True) else: log.debug('provided username:`%s` is empty skipping...', username) return user
def validate_python(self, value, state): from kallithea.lib import auth_modules password = value['password'] username = value['username'] # authenticate returns unused dict but has called # plugin._authenticate which has create_or_update'ed the username user in db if auth_modules.authenticate(username, password) is None: user = User.get_by_username_or_email(username) if user and not user.active: log.warning('user %s is disabled', username) msg = M(self, 'invalid_auth', state) raise formencode.Invalid(msg, value, state, error_dict=dict(username='******', password=msg) ) else: log.warning('user %s failed to authenticate', username) msg = M(self, 'invalid_auth', state) raise formencode.Invalid(msg, value, state, error_dict=dict(username='******', password=msg) )
def _authorize(self, environ, start_response, action, repo_name, ip_addr): """Authenticate and authorize user. Since we're dealing with a VCS client and not a browser, we only support HTTP basic authentication, either directly via raw header inspection, or by using container authentication to delegate the authentication to the web server. Returns (user, None) on successful authentication and authorization. Returns (None, wsgi_app) to send the wsgi_app response to the client. """ # Check if anonymous access is allowed. default_user = User.get_default_user(cache=True) is_default_user_allowed = (default_user.active and self._check_permission(action, default_user, repo_name, ip_addr)) if is_default_user_allowed: return default_user, None if not default_user.active: log.debug('Anonymous access is disabled') else: log.debug('Not authorized to access this ' 'repository as anonymous user') username = None #============================================================== # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS #============================================================== # try to auth based on environ, container auth methods log.debug('Running PRE-AUTH for container based authentication') pre_auth = auth_modules.authenticate('', '', environ) if pre_auth is not None and pre_auth.get('username'): username = pre_auth['username'] log.debug('PRE-AUTH got %s as username', username) # If not authenticated by the container, running basic auth if not username: self.authenticate.realm = safe_str(self.config['realm']) result = self.authenticate(environ) if isinstance(result, str): paste.httpheaders.AUTH_TYPE.update(environ, 'basic') paste.httpheaders.REMOTE_USER.update(environ, result) username = result else: return None, result.wsgi_application #============================================================== # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME #============================================================== try: user = User.get_by_username_or_email(username) if user is None or not user.active: return None, webob.exc.HTTPForbidden() except Exception: log.error(traceback.format_exc()) return None, webob.exc.HTTPInternalServerError() #check permissions for this repository perm = self._check_permission(action, user, repo_name, ip_addr) if not perm: return None, webob.exc.HTTPForbidden() return user, None
def _handle_request(self, environ, start_response): if not is_git(environ): return self.application(environ, start_response) if not self._check_ssl(environ): return HTTPNotAcceptable('SSL REQUIRED !')(environ, start_response) ip_addr = self._get_ip_addr(environ) username = None self._git_first_op = False # skip passing error to error controller environ['pylons.status_code_redirect'] = True #====================================================================== # EXTRACT REPOSITORY NAME FROM ENV #====================================================================== try: str_repo_name = self.__get_repository(environ) repo_name = safe_unicode(str_repo_name) log.debug('Extracted repo name is %s', repo_name) except Exception as e: log.error('error extracting repo_name: %r', e) return HTTPInternalServerError()(environ, start_response) # quick check if that dir exists... if not is_valid_repo(repo_name, self.basepath, 'git'): return HTTPNotFound()(environ, start_response) #====================================================================== # GET ACTION PULL or PUSH #====================================================================== action = self.__get_action(environ) #====================================================================== # CHECK ANONYMOUS PERMISSION #====================================================================== if action in ['pull', 'push']: anonymous_user = User.get_default_user(cache=True) username = anonymous_user.username if anonymous_user.active: # ONLY check permissions if the user is activated anonymous_perm = self._check_permission(action, anonymous_user, repo_name, ip_addr) else: anonymous_perm = False if not anonymous_user.active or not anonymous_perm: if not anonymous_user.active: log.debug('Anonymous access is disabled, running ' 'authentication') if not anonymous_perm: log.debug('Not enough credentials to access this ' 'repository as anonymous user') username = None #============================================================== # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS #============================================================== # try to auth based on environ, container auth methods log.debug('Running PRE-AUTH for container based authentication') pre_auth = auth_modules.authenticate('', '', environ) if pre_auth is not None and pre_auth.get('username'): username = pre_auth['username'] log.debug('PRE-AUTH got %s as username', username) # If not authenticated by the container, running basic auth if not username: self.authenticate.realm = \ safe_str(self.config['realm']) result = self.authenticate(environ) if isinstance(result, str): AUTH_TYPE.update(environ, 'basic') REMOTE_USER.update(environ, result) username = result else: return result.wsgi_application(environ, start_response) #============================================================== # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME #============================================================== try: user = User.get_by_username_or_email(username) if user is None or not user.active: return HTTPForbidden()(environ, start_response) username = user.username except Exception: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response) #check permissions for this repository perm = self._check_permission(action, user, repo_name, ip_addr) if not perm: return HTTPForbidden()(environ, start_response) # extras are injected into UI object and later available # in hooks executed by kallithea from kallithea import CONFIG server_url = get_server_url(environ) extras = { 'ip': ip_addr, 'username': username, 'action': action, 'repository': repo_name, 'scm': 'git', 'config': CONFIG['__file__'], 'server_url': server_url, 'make_lock': None, 'locked_by': [None, None] } #=================================================================== # GIT REQUEST HANDLING #=================================================================== repo_path = os.path.join(safe_str(self.basepath),str_repo_name) log.debug('Repository path is %s', repo_path) # CHECK LOCKING only if it's not ANONYMOUS USER if username != User.DEFAULT_USER: log.debug('Checking locking on repository') (make_lock, locked, locked_by) = self._check_locking_state( environ=environ, action=action, repo=repo_name, user_id=user.user_id ) # store the make_lock for later evaluation in hooks extras.update({'make_lock': make_lock, 'locked_by': locked_by}) fix_PATH() log.debug('HOOKS extras is %s', extras) baseui = make_ui('db') self.__inject_extras(repo_path, baseui, extras) try: self._handle_githooks(repo_name, action, baseui, environ) log.info('%s action on Git repo "%s" by "%s" from %s', action, str_repo_name, safe_str(username), ip_addr) app = self.__make_app(repo_name, repo_path, extras) result = app(environ, start_response) if action == 'push': result = WSGIResultCloseCallback(result, lambda: self._invalidate_cache(repo_name)) return result except HTTPLockedRC as e: _code = CONFIG.get('lock_ret_code') log.debug('Repository LOCKED ret code %s!', _code) return e(environ, start_response) except Exception: log.error(traceback.format_exc()) return HTTPInternalServerError()(environ, start_response)