def _store_user_in_session(self, username, remember=False): user = User.get_by_username(username, case_insensitive=True) auth_user = AuthUser(user.user_id) auth_user.set_authenticated() cs = auth_user.get_cookie_store() session['authuser'] = cs user.update_lastlogin() Session().commit() # If they want to be remembered, update the cookie if remember: _year = (datetime.datetime.now() + datetime.timedelta(seconds=60 * 60 * 24 * 365)) session._set_cookie_expires(_year) session.save() log.info('user %s is now authenticated and stored in ' 'session, session attrs %s' % (username, cs)) # dumps session attrs back to cookie session._update_cookie_out() # we set new cookie headers = None if session.request['set_cookie']: # send set-cookie headers back to response to update cookie headers = [('Set-Cookie', session.request['cookie_out'])] return headers
def get_all_user_repos(self, user): """ Gets all repositories that user have at least read access :param user: """ from kallithea.lib.auth import AuthUser user = self._get_user(user) repos = AuthUser(user_id=user.user_id).permissions['repositories'] access_check = lambda r: r[1] in ['repository.read', 'repository.write', 'repository.admin'] repos = [x[0] for x in filter(access_check, repos.items())] return Repository.query().filter(Repository.repo_name.in_(repos))
def _check_permission(self, action, user, repo_name, ip_addr=None): """ Checks permissions using action (push/pull) user and repository name :param action: push or pull action :param user: `User` instance :param repo_name: repository name """ # check IP ip_allowed = AuthUser.check_ip_allowed(user, ip_addr) if ip_allowed: log.info('Access for IP:%s allowed', ip_addr) else: return False if action == 'push': if not HasPermissionAnyMiddleware('repository.write', 'repository.admin')(user, repo_name): return False else: #any other action need at least read permission if not HasPermissionAnyMiddleware('repository.read', 'repository.write', 'repository.admin')(user, repo_name): return False return True
def _render_edit_profile(self, user): c.user = user c.active = 'profile' c.perm_user = AuthUser(dbuser=user) managed_fields = auth_modules.get_managed_fields(user) c.readonly = lambda n: 'readonly' if n in managed_fields else None return render('admin/users/user_edit.html')
def test_inactive_user_group_does_not_affect_repo_permissions_inverse( self): self.ug1 = fixture.create_user_group('G1') user_group_model = UserGroupModel() user_group_model.add_user_to_group(self.ug1, self.u1) user_group_model.update(self.ug1, {'users_group_active': False}) # note: make u2 repo owner rather than u1, because the owner always has # admin permissions self.test_repo = fixture.create_repo(name='myownrepo', repo_type='hg', cur_user=self.u2) # enable only write access for user group on repo RepoModel().grant_user_group_permission(self.test_repo, group_name=self.ug1, perm='repository.write') # enable admin access for default user on repo RepoModel().grant_user_permission(self.test_repo, user='******', perm='repository.admin') Session().commit() u1_auth = AuthUser(user_id=self.u1.user_id) assert u1_auth.permissions['repositories'][ 'myownrepo'] == 'repository.admin'
def test_inactive_user_group_does_not_affect_global_permissions_inverse( self): # Add user to inactive user group, set specific permissions on user # group and and verify it really is inactive. self.ug1 = fixture.create_user_group('G1') user_group_model = UserGroupModel() user_group_model.add_user_to_group(self.ug1, self.u1) user_group_model.update(self.ug1, {'users_group_active': False}) # disable fork and create on user group user_group_model.revoke_perm(self.ug1, perm='hg.create.repository') user_group_model.grant_perm(self.ug1, perm='hg.create.none') user_group_model.revoke_perm(self.ug1, perm='hg.fork.repository') user_group_model.grant_perm(self.ug1, perm='hg.fork.none') user_model = UserModel() # enable fork and create on default user usr = '******' user_model.revoke_perm(usr, 'hg.create.none') user_model.grant_perm(usr, 'hg.create.repository') user_model.revoke_perm(usr, 'hg.fork.none') user_model.grant_perm(usr, 'hg.fork.repository') Session().commit() u1_auth = AuthUser(user_id=self.u1.user_id) assert u1_auth.permissions['global'] == set([ 'hg.create.repository', 'hg.fork.repository', 'hg.register.manual_activate', 'hg.extern_activate.auto', 'repository.read', 'group.read', 'usergroup.read', 'hg.create.write_on_repogroup.true' ])
def _get_perms(filter_='', recursive=None, key=None, test_u1_id=None): test_u1 = AuthUser(user_id=test_u1_id) for k, v in test_u1.permissions[key].items(): if recursive in ['all', 'repos', 'groups'] and k.startswith(filter_): yield k, v elif recursive in ['none']: if k == filter_: yield k, v
def test_owner_permissions_doesnot_get_overwritten_by_others(self): #create repo as USER, self.test_repo = fixture.create_repo(name='myownrepo', repo_type='hg', cur_user=self.u1) #he has permissions of admin as owner u1_auth = AuthUser(user_id=self.u1.user_id) self.assertEqual(u1_auth.permissions['repositories']['myownrepo'], 'repository.admin') #set his permission as user, he should still be admin RepoModel().grant_user_permission(self.test_repo, user=self.u1, perm='repository.none') Session().commit() u1_auth = AuthUser(user_id=self.u1.user_id) self.assertEqual(u1_auth.permissions['repositories']['myownrepo'], 'repository.admin')
def my_account(self): """ GET /_admin/my_account Displays info about my account """ # url('my_account') c.active = 'profile' self.__load_data() c.perm_user = AuthUser(user_id=self.authuser.user_id, ip_addr=self.ip_addr) c.extern_type = c.user.extern_type c.extern_name = c.user.extern_name defaults = c.user.get_dict() update = False if request.POST: _form = UserForm(edit=True, old_data={ 'user_id': self.authuser.user_id, 'email': self.authuser.email })() form_result = {} try: post_data = dict(request.POST) post_data['new_password'] = '' post_data['password_confirmation'] = '' form_result = _form.to_python(post_data) # skip updating those attrs for my account skip_attrs = [ 'admin', 'active', 'extern_type', 'extern_name', 'new_password', 'password_confirmation' ] #TODO: plugin should define if username can be updated if c.extern_type != EXTERN_TYPE_INTERNAL: # forbid updating username for external accounts skip_attrs.append('username') UserModel().update(self.authuser.user_id, form_result, skip_attrs=skip_attrs) h.flash(_('Your account was updated successfully'), category='success') Session().commit() update = True except formencode.Invalid, errors: return htmlfill.render( render('admin/my_account/my_account.html'), defaults=errors.value, errors=errors.error_dict or {}, prefix_error=False, encoding="UTF-8", force_defaults=False) except Exception: log.error(traceback.format_exc()) h.flash(_('Error occurred during update of user %s') \ % form_result.get('username'), category='error')
def test_default_admin_group_perms(self): self.g1 = fixture.create_repo_group('test1', skip_if_exists=True) self.g2 = fixture.create_repo_group('test2', skip_if_exists=True) a1_auth = AuthUser(user_id=self.a1.user_id) assert a1_auth.permissions['repositories'][ base.HG_REPO] == 'repository.admin' assert a1_auth.permissions['repositories_groups'].get( 'test1') == 'group.admin' assert a1_auth.permissions['repositories_groups'].get( 'test2') == 'group.admin'
def my_account(self): c.active = 'profile' self.__load_data() c.perm_user = AuthUser(user_id=request.authuser.user_id) managed_fields = auth_modules.get_managed_fields(c.user) def_user_perms = User.get_default_user().AuthUser.permissions['global'] if 'hg.register.none' in def_user_perms: managed_fields.extend(['username', 'firstname', 'lastname', 'email']) c.readonly = lambda n: 'readonly' if n in managed_fields else None defaults = c.user.get_dict() update = False if request.POST: _form = UserForm(edit=True, old_data={'user_id': request.authuser.user_id, 'email': request.authuser.email})() form_result = {} try: post_data = dict(request.POST) post_data['new_password'] = '' post_data['password_confirmation'] = '' form_result = _form.to_python(post_data) # skip updating those attrs for my account skip_attrs = ['admin', 'active', 'extern_type', 'extern_name', 'new_password', 'password_confirmation', ] + managed_fields UserModel().update(request.authuser.user_id, form_result, skip_attrs=skip_attrs) h.flash(_('Your account was updated successfully'), category='success') Session().commit() update = True except formencode.Invalid as errors: return htmlfill.render( render('admin/my_account/my_account.html'), defaults=errors.value, errors=errors.error_dict or {}, prefix_error=False, encoding="UTF-8", force_defaults=False) except Exception: log.error(traceback.format_exc()) h.flash(_('Error occurred during update of user %s') \ % form_result.get('username'), category='error') if update: raise HTTPFound(location='my_account') return htmlfill.render( render('admin/my_account/my_account.html'), defaults=defaults, encoding="UTF-8", force_defaults=False)
def _determine_auth_user(session_authuser, ip_addr): """ Create an `AuthUser` object given the API key/bearer token (if any) and the value of the authuser session cookie. Returns None if no valid user is found (like not active or no access for IP). """ # Authenticate by session cookie # In ancient login sessions, 'authuser' may not be a dict. # In that case, the user will have to log in again. # v0.3 and earlier included an 'is_authenticated' key; if present, # this must be True. if isinstance(session_authuser, dict) and session_authuser.get( 'is_authenticated', True): return AuthUser.from_cookie(session_authuser, ip_addr=ip_addr) # Authenticate by auth_container plugin (if enabled) if any(plugin.is_container_auth for plugin in auth_modules.get_auth_plugins()): try: user_info = auth_modules.authenticate('', '', request.environ) except UserCreationError as e: from kallithea.lib import helpers as h h.flash(e, 'error', logf=log.error) else: if user_info is not None: username = user_info['username'] user = User.get_by_username(username, case_insensitive=True) return log_in_user(user, remember=False, is_external_auth=True, ip_addr=ip_addr) # User is default user (if active) or anonymous default_user = User.get_default_user() authuser = AuthUser.make(dbuser=default_user, ip_addr=ip_addr) if authuser is None: # fall back to anonymous authuser = AuthUser( dbuser=default_user) # TODO: somehow use .make? return authuser
def test_default_admin_perms_set(self): a1_auth = AuthUser(user_id=self.a1.user_id) perms = { 'repositories_groups': {}, 'global': set([u'hg.admin', 'hg.create.write_on_repogroup.true']), 'repositories': { u'vcs_test_hg': u'repository.admin' } } self.assertEqual(a1_auth.permissions['repositories'][HG_REPO], perms['repositories'][HG_REPO]) new_perm = 'repository.write' RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1, perm=new_perm) Session().commit() # cannot really downgrade admins permissions !? they still gets set as # admin ! u1_auth = AuthUser(user_id=self.a1.user_id) self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], perms['repositories'][HG_REPO])
def test_default_group_perms(self): self.g1 = fixture.create_repo_group('test1', skip_if_exists=True) self.g2 = fixture.create_repo_group('test2', skip_if_exists=True) u1_auth = AuthUser(user_id=self.u1.user_id) assert u1_auth.permissions['repositories'][ base.HG_REPO] == 'repository.read' assert u1_auth.permissions['repositories_groups'].get( 'test1') == 'group.read' assert u1_auth.permissions['repositories_groups'].get( 'test2') == 'group.read' assert u1_auth.permissions['global'] == set( Permission.DEFAULT_USER_PERMISSIONS)
def test_owner_permissions_doesnot_get_overwritten_by_group(self): #create repo as USER, self.test_repo = fixture.create_repo(name=u'myownrepo', repo_type='hg', cur_user=self.u1) #he has permissions of admin as owner u1_auth = AuthUser(user_id=self.u1.user_id) assert u1_auth.permissions['repositories'][ 'myownrepo'] == 'repository.admin' #set his permission as user group, he should still be admin self.ug1 = fixture.create_user_group(u'G1') UserGroupModel().add_user_to_group(self.ug1, self.u1) RepoModel().grant_user_group_permission(self.test_repo, group_name=self.ug1, perm='repository.none') Session().commit() u1_auth = AuthUser(user_id=self.u1.user_id) assert u1_auth.permissions['repositories'][ 'myownrepo'] == 'repository.admin'
def __call__(self, environ, start_response): """Invoke the Controller""" # WSGIController.__call__ dispatches to the Controller method # the request is routed to. This routing information is # available in environ['pylons.routes_dict'] try: self.ip_addr = _get_ip_addr(environ) # make sure that we update permissions each time we call controller api_key = request.GET.get('api_key') if api_key: # when using API_KEY we are sure user exists. auth_user = AuthUser(api_key=api_key, ip_addr=self.ip_addr) authenticated = False else: cookie_store = CookieStoreWrapper(session.get('authuser')) try: auth_user = AuthUser(user_id=cookie_store.get('user_id', None), ip_addr=self.ip_addr) except UserCreationError, e: from kallithea.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) authenticated = cookie_store.get('is_authenticated') if not auth_user.is_authenticated and auth_user.user_id is not None: # user is not authenticated and not empty auth_user.set_authenticated(authenticated) request.user = auth_user #set globals for auth user self.authuser = c.authuser = auth_user log.info('IP: %s User: %s accessed %s' % ( self.ip_addr, auth_user, safe_unicode(_get_access_path(environ))) ) return WSGIController.__call__(self, environ, start_response)
def register(self): def_user_perms = AuthUser( dbuser=User.get_default_user()).permissions['global'] c.auto_active = 'hg.register.auto_activate' in def_user_perms settings = Setting.get_app_settings() captcha_private_key = settings.get('captcha_private_key') c.captcha_active = bool(captcha_private_key) c.captcha_public_key = settings.get('captcha_public_key') if request.POST: register_form = RegisterForm()() try: form_result = register_form.to_python(dict(request.POST)) form_result['active'] = c.auto_active if c.captcha_active: from kallithea.lib.recaptcha import submit response = submit(request.POST.get('g-recaptcha-response'), private_key=captcha_private_key, remoteip=request.ip_addr) if not response.is_valid: _value = form_result _msg = _('Bad captcha') error_dict = {'recaptcha_field': _msg} raise formencode.Invalid(_msg, _value, None, error_dict=error_dict) UserModel().create_registration(form_result) h.flash(_('You have successfully registered with %s') % (c.site_name or 'Kallithea'), category='success') Session().commit() raise HTTPFound(location=url('login_home')) except formencode.Invalid as errors: return htmlfill.render(render('/register.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') return render('/register.html')
def test_propagated_permission_from_users_group(self): # make group self.ug1 = fixture.create_user_group('G1') UserGroupModel().add_user_to_group(self.ug1, self.u3) # grant perm for group this should override default permission from user new_perm_gr = 'repository.write' RepoModel().grant_user_group_permission(repo=base.HG_REPO, group_name=self.ug1, perm=new_perm_gr) # check perms u3_auth = AuthUser(user_id=self.u3.user_id) assert u3_auth.permissions['repositories'][base.HG_REPO] == new_perm_gr
def test_propagated_permission_from_users_group_lower_weight(self): # make group self.ug1 = fixture.create_user_group('G1') # add user to group UserGroupModel().add_user_to_group(self.ug1, self.u1) # set permission to lower new_perm_h = 'repository.write' RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm_h) Session().commit() u1_auth = AuthUser(user_id=self.u1.user_id) self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm_h) # grant perm for group this should NOT override permission from user # since it's lower than granted new_perm_l = 'repository.read' RepoModel().grant_user_group_permission(repo=HG_REPO, group_name=self.ug1, perm=new_perm_l) # check perms u1_auth = AuthUser(user_id=self.u1.user_id) perms = { 'repositories_groups': {}, 'global': set([ u'hg.create.repository', u'repository.read', u'hg.register.manual_activate' ]), 'repositories': { u'vcs_test_hg': u'repository.write' } } self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm_h) self.assertEqual(u1_auth.permissions['repositories_groups'], perms['repositories_groups'])
def update(self, id): """PUT /users/id: Update an existing item""" # Forms posted to this method should contain a hidden field: # <input type="hidden" name="_method" value="PUT" /> # Or using helpers: # h.form(url('update_user', id=ID), # method='put') # url('user', id=ID) c.active = 'profile' user_model = UserModel() c.user = user_model.get(id) c.extern_type = c.user.extern_type c.extern_name = c.user.extern_name c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr) _form = UserForm(edit=True, old_data={ 'user_id': id, 'email': c.user.email })() form_result = {} try: form_result = _form.to_python(dict(request.POST)) skip_attrs = ['extern_type', 'extern_name'] #TODO: plugin should define if username can be updated if c.extern_type != kallithea.EXTERN_TYPE_INTERNAL: # forbid updating username for external accounts skip_attrs.append('username') user_model.update(id, form_result, skip_attrs=skip_attrs) usr = form_result['username'] action_logger(self.authuser, 'admin_updated_user:%s' % usr, None, self.ip_addr, self.sa) h.flash(_('User updated successfully'), category='success') Session().commit() except formencode.Invalid, errors: defaults = errors.value e = errors.error_dict or {} defaults.update({ 'create_repo_perm': user_model.has_perm(id, 'hg.create.repository'), 'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'), '_method': 'put' }) return htmlfill.render(render('admin/users/user_edit.html'), defaults=defaults, errors=e, prefix_error=False, encoding="UTF-8", force_defaults=False)
def __call__(self, environ, context): try: ip_addr = _get_ip_addr(environ) self._basic_security_checks() api_key = request.GET.get('api_key') try: # Request.authorization may raise ValueError on invalid input type, params = request.authorization except (ValueError, TypeError): pass else: if type.lower() == 'bearer': api_key = params # bearer token is an api key too if api_key is None: authuser = self._determine_auth_user( session.get('authuser'), ip_addr=ip_addr, ) needs_csrf_check = request.method not in ['GET', 'HEAD'] else: dbuser = User.get_by_api_key(api_key) if dbuser is None: log.info( 'No db user found for authentication with API key ****%s from %s', api_key[-4:], ip_addr) authuser = AuthUser.make(dbuser=dbuser, is_external_auth=True, ip_addr=ip_addr) needs_csrf_check = False # API key provides CSRF protection if authuser is None: log.info('No valid user found') raise webob.exc.HTTPForbidden() # set globals for auth user request.authuser = authuser request.ip_addr = ip_addr request.needs_csrf_check = needs_csrf_check log.info( 'IP: %s User: %s Request: %s', request.ip_addr, request.authuser, get_path_info(environ), ) return super(BaseController, self).__call__(environ, context) except webob.exc.HTTPException as e: return e
def get_all_user_repos(self, user): """ Gets all repositories that user have at least read access :param user: """ from kallithea.lib.auth import AuthUser auth_user = AuthUser(dbuser=User.guess_instance(user)) repos = [ repo_name for repo_name, perm in auth_user.permissions['repositories'].items() if perm in ['repository.read', 'repository.write', 'repository.admin'] ] return Repository.query().filter(Repository.repo_name.in_(repos))
def test_default_perms_set(self): u1_auth = AuthUser(user_id=self.u1.user_id) perms = { 'repositories_groups': {}, 'global': set([ 'hg.create.repository', 'repository.read', 'hg.register.manual_activate' ]), 'repositories': { HG_REPO: 'repository.read' } } assert u1_auth.permissions['repositories'][HG_REPO] == perms[ 'repositories'][HG_REPO] new_perm = 'repository.write' RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm) Session().commit() u1_auth = AuthUser(user_id=self.u1.user_id) assert u1_auth.permissions['repositories'][HG_REPO] == new_perm
def test_propagated_permission_from_users_group_by_explicit_perms_exist( self): # make group self.ug1 = fixture.create_user_group(u'G1') UserGroupModel().add_user_to_group(self.ug1, self.u1) # set permission to lower new_perm = 'repository.none' RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm) Session().commit() u1_auth = AuthUser(user_id=self.u1.user_id) assert u1_auth.permissions['repositories'][HG_REPO] == new_perm # grant perm for group this should not override permission from user # since it has explicitly set new_perm_gr = 'repository.write' RepoModel().grant_user_group_permission(repo=HG_REPO, group_name=self.ug1, perm=new_perm_gr) # check perms u1_auth = AuthUser(user_id=self.u1.user_id) perms = { 'repositories_groups': {}, 'global': set([ 'hg.create.repository', 'repository.read', 'hg.register.manual_activate' ]), 'repositories': { HG_REPO: 'repository.read' } } assert u1_auth.permissions['repositories'][HG_REPO] == new_perm assert u1_auth.permissions['repositories_groups'] == perms[ 'repositories_groups']
def log_in_user(user, remember, is_external_auth): """ Log a `User` in and update session and cookies. If `remember` is True, the session cookie is set to expire in a year; otherwise, it expires at the end of the browser session. Returns populated `AuthUser` object. """ user.update_lastlogin() meta.Session().commit() auth_user = AuthUser(dbuser=user, is_external_auth=is_external_auth) # It should not be possible to explicitly log in as the default user. assert not auth_user.is_default_user auth_user.is_authenticated = True # Start new session to prevent session fixation attacks. session.invalidate() session['authuser'] = cookie = auth_user.to_cookie() # If they want to be remembered, update the cookie. # NOTE: Assumes that beaker defaults to browser session cookie. if remember: t = datetime.datetime.now() + datetime.timedelta(days=365) session._set_cookie_expires(t) session.save() log.info('user %s is now authenticated and stored in ' 'session, session attrs %s', user.username, cookie) # dumps session attrs back to cookie session._update_cookie_out() return auth_user
def test_propagated_permission_from_users_group_lower_weight(self): # make group self.ug1 = fixture.create_user_group('G1') # add user to group UserGroupModel().add_user_to_group(self.ug1, self.u1) # set permission to lower new_perm_h = 'repository.write' RepoModel().grant_user_permission(repo=base.HG_REPO, user=self.u1, perm=new_perm_h) Session().commit() u1_auth = AuthUser(user_id=self.u1.user_id) assert u1_auth.permissions['repositories'][base.HG_REPO] == new_perm_h # grant perm for group this should NOT override permission from user # since it's lower than granted new_perm_l = 'repository.read' RepoModel().grant_user_group_permission(repo=base.HG_REPO, group_name=self.ug1, perm=new_perm_l) # check perms u1_auth = AuthUser(user_id=self.u1.user_id) assert u1_auth.permissions['repositories'][base.HG_REPO] == new_perm_h
def test_default_perms_set(self): u1_auth = AuthUser(user_id=self.u1.user_id) perms = { 'repositories_groups': {}, 'global': set([ u'hg.create.repository', u'repository.read', u'hg.register.manual_activate' ]), 'repositories': { u'vcs_test_hg': u'repository.read' } } self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], perms['repositories'][HG_REPO]) new_perm = 'repository.write' RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm) Session().commit() u1_auth = AuthUser(user_id=self.u1.user_id) self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm)
def log_in_user(user, remember, is_external_auth): """ Log a `User` in and update session and cookies. If `remember` is True, the session cookie is set to expire in a year; otherwise, it expires at the end of the browser session. Returns populated `AuthUser` object. """ user.update_lastlogin() meta.Session().commit() auth_user = AuthUser(dbuser=user, is_external_auth=is_external_auth) # It should not be possible to explicitly log in as the default user. assert not auth_user.is_default_user auth_user.is_authenticated = True # Start new session to prevent session fixation attacks. session.invalidate() session['authuser'] = cookie = auth_user.to_cookie() # If they want to be remembered, update the cookie. # NOTE: Assumes that beaker defaults to browser session cookie. if remember: t = datetime.datetime.now() + datetime.timedelta(days=365) session._set_cookie_expires(t) session.save() log.info('user %s is now authenticated and stored in ' 'session, session attrs %s', user.username, cookie) # dumps session attrs back to cookie session._update_cookie_out() return auth_user
def test_propagated_permission_from_users_group_by_explicit_perms_exist( self): # make group self.ug1 = fixture.create_user_group('G1') UserGroupModel().add_user_to_group(self.ug1, self.u1) # set user permission none RepoModel().grant_user_permission(repo=base.HG_REPO, user=self.u1, perm='repository.none') Session().commit() u1_auth = AuthUser(user_id=self.u1.user_id) assert u1_auth.permissions['repositories'][ base.HG_REPO] == 'repository.read' # inherit from default user # grant perm for group this should override permission from user RepoModel().grant_user_group_permission(repo=base.HG_REPO, group_name=self.ug1, perm='repository.write') # verify that user group permissions win u1_auth = AuthUser(user_id=self.u1.user_id) assert u1_auth.permissions['repositories'][ base.HG_REPO] == 'repository.write'
def test_inherit_sad_permissions_from_default_user(self): user_model = UserModel() # disable fork and create on default user usr = '******' user_model.revoke_perm(usr, 'hg.create.repository') user_model.grant_perm(usr, 'hg.create.none') user_model.revoke_perm(usr, 'hg.fork.repository') user_model.grant_perm(usr, 'hg.fork.none') Session().commit() u1_auth = AuthUser(user_id=self.u1.user_id) # this user will have inherited permissions from default user assert u1_auth.permissions['global'] == set([ 'hg.create.none', 'hg.fork.none', 'hg.register.manual_activate', 'hg.extern_activate.auto', 'repository.read', 'group.read', 'usergroup.read', 'hg.create.write_on_repogroup.true' ])
def _determine_auth_user(api_key, session_authuser): """ Create an `AuthUser` object given the API key (if any) and the value of the authuser session cookie. """ # Authenticate by API key if api_key: # when using API_KEY we are sure user exists. return AuthUser(dbuser=User.get_by_api_key(api_key), is_external_auth=True) # Authenticate by session cookie # In ancient login sessions, 'authuser' may not be a dict. # In that case, the user will have to log in again. # v0.3 and earlier included an 'is_authenticated' key; if present, # this must be True. if isinstance(session_authuser, dict) and session_authuser.get('is_authenticated', True): try: return AuthUser.from_cookie(session_authuser) except UserCreationError as e: # container auth or other auth functions that create users on # the fly can throw UserCreationError to signal issues with # user creation. Explanation should be provided in the # exception object. from kallithea.lib import helpers as h h.flash(e, 'error', logf=log.error) # Authenticate by auth_container plugin (if enabled) if any( auth_modules.importplugin(name).is_container_auth for name in Setting.get_auth_plugins() ): try: user_info = auth_modules.authenticate('', '', request.environ) except UserCreationError as e: from kallithea.lib import helpers as h h.flash(e, 'error', logf=log.error) else: if user_info is not None: username = user_info['username'] user = User.get_by_username(username, case_insensitive=True) return log_in_user(user, remember=False, is_external_auth=True) # User is anonymous return AuthUser()
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 edit_perms(self, id): c.user = self._get_user_or_raise_if_default(id) c.active = 'perms' c.perm_user = AuthUser(dbuser=c.user) umodel = UserModel() defaults = c.user.get_dict() defaults.update({ 'create_repo_perm': umodel.has_perm(c.user, 'hg.create.repository'), 'create_user_group_perm': umodel.has_perm(c.user, 'hg.usergroup.create.true'), 'fork_repo_perm': umodel.has_perm(c.user, 'hg.fork.repository'), }) return htmlfill.render( render('admin/users/user_edit.html'), defaults=defaults, encoding="UTF-8", force_defaults=False)
def edit(self, id, format='html'): """GET /users/id/edit: Form to edit an existing item""" # url('edit_user', id=ID) c.user = User.get_or_404(id) if c.user.username == User.DEFAULT_USER: h.flash(_("You can't edit this user"), category='warning') return redirect(url('users')) c.active = 'profile' c.extern_type = c.user.extern_type c.extern_name = c.user.extern_name c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr) defaults = c.user.get_dict() return htmlfill.render(render('admin/users/user_edit.html'), defaults=defaults, encoding="UTF-8", force_defaults=False)
def __call__(self, environ, start_response): """Invoke the Controller""" # WSGIController.__call__ dispatches to the Controller method # the request is routed to. This routing information is # available in environ['pylons.routes_dict'] try: self.ip_addr = _get_ip_addr(environ) # make sure that we update permissions each time we call controller api_key = request.GET.get('api_key') if api_key: # when using API_KEY we are sure user exists. auth_user = AuthUser(api_key=api_key, ip_addr=self.ip_addr) authenticated = False else: cookie_store = CookieStoreWrapper(session.get('authuser')) try: auth_user = AuthUser(user_id=cookie_store.get( 'user_id', None), ip_addr=self.ip_addr) except UserCreationError, e: from kallithea.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) authenticated = cookie_store.get('is_authenticated') if not auth_user.is_authenticated and auth_user.user_id is not None: # user is not authenticated and not empty auth_user.set_authenticated(authenticated) request.user = auth_user #set globals for auth user self.authuser = c.authuser = auth_user log.info('IP: %s User: %s accessed %s' % (self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))) return WSGIController.__call__(self, environ, start_response)
def test_default_admin_group_perms(self): self.g1 = fixture.create_repo_group('test1', skip_if_exists=True) self.g2 = fixture.create_repo_group('test2', skip_if_exists=True) a1_auth = AuthUser(user_id=self.a1.user_id) perms = { 'repositories_groups': { u'test1': 'group.admin', u'test2': 'group.admin' }, 'global': set(['hg.admin', 'hg.create.write_on_repogroup.true']), 'repositories': { u'vcs_test_hg': 'repository.admin' } } self.assertEqual(a1_auth.permissions['repositories'][HG_REPO], perms['repositories'][HG_REPO]) self.assertEqual(a1_auth.permissions['repositories_groups'], perms['repositories_groups'])
def _dispatch(self, state, remainder=None): """ Parse the request body as JSON, look up the method on the controller and if it exists, dispatch to it. """ # Since we are here we should respond as JSON response.content_type = 'application/json' environ = state.request.environ start = time.time() ip_addr = request.ip_addr = self._get_ip_addr(environ) self._req_id = None if 'CONTENT_LENGTH' not in environ: log.debug("No Content-Length") raise JSONRPCErrorResponse(retid=self._req_id, message="No Content-Length in request") else: length = environ['CONTENT_LENGTH'] or 0 length = int(environ['CONTENT_LENGTH']) log.debug('Content-Length: %s', length) if length == 0: raise JSONRPCErrorResponse(retid=self._req_id, message="Content-Length is 0") raw_body = environ['wsgi.input'].read(length) try: json_body = json.loads(raw_body) except ValueError as e: # catch JSON errors Here raise JSONRPCErrorResponse(retid=self._req_id, message="JSON parse error ERR:%s RAW:%r" % (e, raw_body)) # check AUTH based on API key try: self._req_api_key = json_body['api_key'] self._req_id = json_body['id'] self._req_method = json_body['method'] self._request_params = json_body['args'] if not isinstance(self._request_params, dict): self._request_params = {} log.debug('method: %s, params: %s', self._req_method, self._request_params) except KeyError as e: raise JSONRPCErrorResponse(retid=self._req_id, message='Incorrect JSON query missing %s' % e) # check if we can find this session using api_key try: u = User.get_by_api_key(self._req_api_key) if u is None: raise JSONRPCErrorResponse(retid=self._req_id, message='Invalid API key') auth_u = AuthUser(dbuser=u) if not AuthUser.check_ip_allowed(auth_u, ip_addr): raise JSONRPCErrorResponse(retid=self._req_id, message='request from IP:%s not allowed' % (ip_addr,)) else: log.info('Access for IP:%s allowed', ip_addr) except Exception as e: raise JSONRPCErrorResponse(retid=self._req_id, message='Invalid API key') self._error = None try: self._func = self._find_method() except AttributeError as e: raise JSONRPCErrorResponse(retid=self._req_id, message=str(e)) # now that we have a method, add self._req_params to # self.kargs and dispatch control to WGIController argspec = inspect.getargspec(self._func) arglist = argspec[0][1:] defaults = map(type, argspec[3] or []) default_empty = types.NotImplementedType # kw arguments required by this method func_kwargs = dict(itertools.izip_longest(reversed(arglist), reversed(defaults), fillvalue=default_empty)) # this is little trick to inject logged in user for # perms decorators to work they expect the controller class to have # authuser attribute set request.authuser = request.user = auth_u # This attribute will need to be first param of a method that uses # api_key, which is translated to instance of user at that name USER_SESSION_ATTR = 'apiuser' # get our arglist and check if we provided them as args for arg, default in func_kwargs.iteritems(): if arg == USER_SESSION_ATTR: # USER_SESSION_ATTR is something translated from API key and # this is checked before so we don't need validate it continue # skip the required param check if it's default value is # NotImplementedType (default_empty) if default == default_empty and arg not in self._request_params: raise JSONRPCErrorResponse( retid=self._req_id, message='Missing non optional `%s` arg in JSON DATA' % arg, ) extra = set(self._request_params).difference(func_kwargs) if extra: raise JSONRPCErrorResponse( retid=self._req_id, message='Unknown %s arg in JSON DATA' % ', '.join('`%s`' % arg for arg in extra), ) self._rpc_args = {} self._rpc_args.update(self._request_params) self._rpc_args['action'] = self._req_method self._rpc_args['environ'] = environ log.info('IP: %s Request to %s time: %.3fs' % ( self._get_ip_addr(environ), safe_unicode(_get_access_path(environ)), time.time() - start) ) state.set_action(self._rpc_call, []) state.set_params(self._rpc_args) return state
def _handle_request(self, environ, start_response): start = time.time() ip_addr = self.ip_addr = self._get_ip_addr(environ) self._req_id = None if 'CONTENT_LENGTH' not in environ: log.debug("No Content-Length") return jsonrpc_error(retid=self._req_id, message="No Content-Length in request") else: length = environ['CONTENT_LENGTH'] or 0 length = int(environ['CONTENT_LENGTH']) log.debug('Content-Length: %s', length) if length == 0: log.debug("Content-Length is 0") return jsonrpc_error(retid=self._req_id, message="Content-Length is 0") raw_body = environ['wsgi.input'].read(length) try: json_body = json.loads(raw_body) except ValueError as e: # catch JSON errors Here return jsonrpc_error(retid=self._req_id, message="JSON parse error ERR:%s RAW:%r" % (e, raw_body)) # check AUTH based on API key try: self._req_api_key = json_body['api_key'] self._req_id = json_body['id'] self._req_method = json_body['method'] self._request_params = json_body['args'] if not isinstance(self._request_params, dict): self._request_params = {} log.debug( 'method: %s, params: %s', self._req_method, self._request_params ) except KeyError as e: return jsonrpc_error(retid=self._req_id, message='Incorrect JSON query missing %s' % e) # check if we can find this session using api_key try: u = User.get_by_api_key(self._req_api_key) if u is None: return jsonrpc_error(retid=self._req_id, message='Invalid API key') auth_u = AuthUser(dbuser=u) if not AuthUser.check_ip_allowed(auth_u, ip_addr): return jsonrpc_error(retid=self._req_id, message='request from IP:%s not allowed' % (ip_addr,)) else: log.info('Access for IP:%s allowed', ip_addr) except Exception as e: return jsonrpc_error(retid=self._req_id, message='Invalid API key') self._error = None try: self._func = self._find_method() except AttributeError as e: return jsonrpc_error(retid=self._req_id, message=str(e)) # now that we have a method, add self._req_params to # self.kargs and dispatch control to WGIController argspec = inspect.getargspec(self._func) arglist = argspec[0][1:] defaults = map(type, argspec[3] or []) default_empty = types.NotImplementedType # kw arguments required by this method func_kwargs = dict(izip_longest(reversed(arglist), reversed(defaults), fillvalue=default_empty)) # this is little trick to inject logged in user for # perms decorators to work they expect the controller class to have # authuser attribute set self.authuser = auth_u # This attribute will need to be first param of a method that uses # api_key, which is translated to instance of user at that name USER_SESSION_ATTR = 'apiuser' if USER_SESSION_ATTR not in arglist: return jsonrpc_error( retid=self._req_id, message='This method [%s] does not support ' 'authentication (missing %s param)' % ( self._func.__name__, USER_SESSION_ATTR) ) # get our arglist and check if we provided them as args for arg, default in func_kwargs.iteritems(): if arg == USER_SESSION_ATTR: # USER_SESSION_ATTR is something translated from API key and # this is checked before so we don't need validate it continue # skip the required param check if it's default value is # NotImplementedType (default_empty) if default == default_empty and arg not in self._request_params: return jsonrpc_error( retid=self._req_id, message=( 'Missing non optional `%s` arg in JSON DATA' % arg ) ) self._rpc_args = {USER_SESSION_ATTR: u} self._rpc_args.update(self._request_params) self._rpc_args['action'] = self._req_method self._rpc_args['environ'] = environ self._rpc_args['start_response'] = start_response status = [] headers = [] exc_info = [] def change_content(new_status, new_headers, new_exc_info=None): status.append(new_status) headers.extend(new_headers) exc_info.append(new_exc_info) output = WSGIController.__call__(self, environ, change_content) output = list(output) headers.append(('Content-Length', str(len(output[0])))) replace_header(headers, 'Content-Type', 'application/json') start_response(status[0], headers, exc_info[0]) log.info('IP: %s Request to %s time: %.3fs' % ( self._get_ip_addr(environ), safe_unicode(_get_access_path(environ)), time.time() - start) ) return output