def test_delete(self):
        self.log_user()
        cur_user = self._get_logged_user()

        u1 = UserModel().create_or_update(username='******', password='******',
                                               email='*****@*****.**',
                                               firstname='u1', lastname='u1')
        u2 = UserModel().create_or_update(username='******', password='******',
                                               email='*****@*****.**',
                                               firstname='u2', lastname='u2')

        # make notifications
        notification = NotificationModel().create(created_by=cur_user,
                                                  subject=u'test',
                                                  body=u'hi there',
                                                  recipients=[cur_user, u1, u2])
        self.Session().commit()
        u1 = User.get(u1.user_id)
        u2 = User.get(u2.user_id)

        # check DB
        get_notif = lambda un: [x.notification for x in un]
        self.assertEqual(get_notif(cur_user.notifications), [notification])
        self.assertEqual(get_notif(u1.notifications), [notification])
        self.assertEqual(get_notif(u2.notifications), [notification])
        cur_usr_id = cur_user.user_id

        response = self.app.delete(url('notification',
                                       notification_id=
                                       notification.notification_id))

        cur_user = User.get(cur_usr_id)
        self.assertEqual(cur_user.notifications, [])
Пример #2
0
    def test_create_notification(self):
        self.assertEqual([], Notification.query().all())
        self.assertEqual([], UserNotification.query().all())

        usrs = [self.u1, self.u2]
        notification = NotificationModel().create(created_by=self.u1,
                                                  subject=u'subj',
                                                  body=u'hi there',
                                                  recipients=usrs)
        Session().commit()
        u1 = User.get(self.u1)
        u2 = User.get(self.u2)
        u3 = User.get(self.u3)
        notifications = Notification.query().all()
        self.assertEqual(len(notifications), 1)

        self.assertEqual(notifications[0].recipients, [u1, u2])
        self.assertEqual(notification.notification_id,
                         notifications[0].notification_id)

        unotification = UserNotification.query()\
            .filter(UserNotification.notification == notification).all()

        self.assertEqual(len(unotification), len(usrs))
        self.assertEqual(set([x.user.user_id for x in unotification]),
                         set(usrs))
Пример #3
0
    def test_delete(self):
        self.log_user()
        cur_user = self._get_logged_user()

        u1 = UserModel().create_or_update(
            username="******", password="******", email="*****@*****.**", firstname="u1", lastname="u1"
        )
        u2 = UserModel().create_or_update(
            username="******", password="******", email="*****@*****.**", firstname="u2", lastname="u2"
        )

        # make notifications
        notification = NotificationModel().create(
            created_by=cur_user, subject=u"test", body=u"hi there", recipients=[cur_user, u1, u2]
        )
        Session().commit()
        u1 = User.get(u1.user_id)
        u2 = User.get(u2.user_id)

        # check DB
        get_notif = lambda un: [x.notification for x in un]
        self.assertEqual(get_notif(cur_user.notifications), [notification])
        self.assertEqual(get_notif(u1.notifications), [notification])
        self.assertEqual(get_notif(u2.notifications), [notification])
        cur_usr_id = cur_user.user_id

        response = self.app.delete(url("notification", notification_id=notification.notification_id))
        self.assertEqual(response.body, "ok")

        cur_user = User.get(cur_usr_id)
        self.assertEqual(cur_user.notifications, [])
Пример #4
0
    def test_delete(self):
        self.log_user()
        cur_user = self._get_logged_user()

        u1 = UserModel().create_or_update(username='******', password='******',
                                               email='*****@*****.**',
                                               firstname='u1', lastname='u1')
        u2 = UserModel().create_or_update(username='******', password='******',
                                               email='*****@*****.**',
                                               firstname='u2', lastname='u2')

        # make notifications
        notification = NotificationModel().create(created_by=cur_user,
                                                  subject=u'test',
                                                  body=u'hi there',
                                                  recipients=[cur_user, u1, u2])
        Session().commit()
        u1 = User.get(u1.user_id)
        u2 = User.get(u2.user_id)

        # check DB
        get_notif = lambda un: [x.notification for x in un]
        self.assertEqual(get_notif(cur_user.notifications), [notification])
        self.assertEqual(get_notif(u1.notifications), [notification])
        self.assertEqual(get_notif(u2.notifications), [notification])
        cur_usr_id = cur_user.user_id

        response = self.app.delete(url('notification',
                                       notification_id=
                                       notification.notification_id))
        self.assertEqual(response.body, 'ok')

        cur_user = User.get(cur_usr_id)
        self.assertEqual(cur_user.notifications, [])
    def test_my_account_remove_auth_token(self):
        # TODO: without this cleanup it fails when run with the whole
        # test suite, so there must be some interference with other tests.
        UserApiKeys.query().delete()

        usr = self.log_user('test_regular2', 'test12')
        User.get(usr['user_id'])
        response = self.app.post(url('my_account_auth_tokens'), {
            'description': 'desc',
            'lifetime': -1,
            'csrf_token': self.csrf_token
        })
        assert_session_flash(response, 'Auth token successfully created')
        response = response.follow()

        # now delete our key
        keys = UserApiKeys.query().all()
        assert 1 == len(keys)

        response = self.app.post(
            url('my_account_auth_tokens'), {
                '_method': 'delete',
                'del_auth_token': keys[0].api_key,
                'csrf_token': self.csrf_token
            })
        assert_session_flash(response, 'Auth token successfully deleted')
        keys = UserApiKeys.query().all()
        assert 0 == len(keys)
Пример #6
0
    def test_create_notification(self):
        self.assertEqual([], Notification.query().all())
        self.assertEqual([], UserNotification.query().all())

        usrs = [self.u1, self.u2]
        notification = NotificationModel().create(created_by=self.u1,
                                           subject=u'subj', body=u'hi there',
                                           recipients=usrs)
        Session().commit()
        u1 = User.get(self.u1)
        u2 = User.get(self.u2)
        u3 = User.get(self.u3)
        notifications = Notification.query().all()
        self.assertEqual(len(notifications), 1)

        self.assertEqual(notifications[0].recipients, [u1, u2])
        self.assertEqual(notification.notification_id,
                         notifications[0].notification_id)

        unotification = UserNotification.query()\
            .filter(UserNotification.notification == notification).all()

        self.assertEqual(len(unotification), len(usrs))
        self.assertEqual(set([x.user.user_id for x in unotification]),
                         set(usrs))
Пример #7
0
def log_pull_action(ui, repo, **kwargs):
    """
    Logs user last pull action

    :param ui:
    :param repo:
    """
    ex = _extract_extras()

    user = User.get_by_username(ex.username)
    action = 'pull'
    action_logger(user, action, ex.repository, ex.ip, commit=True)
    # extension hook call
    from rhodecode import EXTENSIONS
    callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
    if isfunction(callback):
        kw = {}
        kw.update(ex)
        callback(**kw)

    if ex.make_lock is not None and ex.make_lock:
        Repository.lock(Repository.get_by_repo_name(ex.repository),
                        user.user_id)
        #msg = 'Made lock on repo `%s`' % repository
        #sys.stdout.write(msg)

    if ex.locked_by[0]:
        locked_by = User.get(ex.locked_by[0]).username
        _http_ret = HTTPLockedRC(ex.repository, locked_by)
        if str(_http_ret.code).startswith('2'):
            #2xx Codes don't raise exceptions
            sys.stdout.write(_http_ret.title)
    return 0
Пример #8
0
 def __load_data(self):
     c.user = User.get(c.rhodecode_user.user_id)
     if c.user.username == User.DEFAULT_USER:
         h.flash(_("You can't edit this user since it's"
                   " crucial for entire application"),
                 category='warning')
         return redirect(url('users'))
Пример #9
0
    def index(self):
        # Return a rendered template
        p = safe_int(request.GET.get('page', 1), 1)
        c.user = User.get(c.rhodecode_user.user_id)
        following = self.sa.query(UserFollowing)\
            .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
            .options(joinedload(UserFollowing.follows_repository))\
            .all()

        journal = self._get_journal_data(following)

        def url_generator(**kw):
            return url.current(filter=c.search_term, **kw)

        c.journal_pager = Page(journal,
                               page=p,
                               items_per_page=20,
                               url=url_generator)
        c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)

        c.journal_data = render('journal/journal_data.html')
        if request.is_xhr:
            return c.journal_data

        return render('journal/journal.html')
Пример #10
0
def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
    """
    Action logger for various actions made by users

    :param user: user that made this action, can be a unique username string or
        object containing user_id attribute
    :param action: action to log, should be on of predefined unique actions for
        easy translations
    :param repo: string name of repository or object containing repo_id,
        that action was made on
    :param ipaddr: optional ip address from what the action was made
    :param sa: optional sqlalchemy session

    """

    if not sa:
        sa = meta.Session()
    # if we don't get explicit IP address try to get one from registered user
    # in tmpl context var
    if not ipaddr:
        ipaddr = getattr(get_current_rhodecode_user(), 'ip_addr', '')

    try:
        if getattr(user, 'user_id', None):
            user_obj = User.get(user.user_id)
        elif isinstance(user, basestring):
            user_obj = User.get_by_username(user)
        else:
            raise Exception('You have to provide a user object or a username')

        if getattr(repo, 'repo_id', None):
            repo_obj = Repository.get(repo.repo_id)
            repo_name = repo_obj.repo_name
        elif isinstance(repo, basestring):
            repo_name = repo.lstrip('/')
            repo_obj = Repository.get_by_repo_name(repo_name)
        else:
            repo_obj = None
            repo_name = ''

        user_log = UserLog()
        user_log.user_id = user_obj.user_id
        user_log.username = user_obj.username
        action = safe_unicode(action)
        user_log.action = action[:1200000]

        user_log.repository = repo_obj
        user_log.repository_name = repo_name

        user_log.action_date = datetime.datetime.now()
        user_log.user_ip = ipaddr
        sa.add(user_log)

        log.info('Logging action:`%s` on repo:`%s` by user:%s ip:%s', action,
                 safe_unicode(repo), user_obj, ipaddr)
        if commit:
            sa.commit()
    except Exception:
        log.error(traceback.format_exc())
        raise
Пример #11
0
    def update_object_permissions(self, form_result):
        if 'perm_user_id' in form_result:
            perm_user = User.get(safe_int(form_result['perm_user_id']))
        else:
            # used mostly to do lookup for default user
            perm_user = User.get_by_username(form_result['perm_user_name'])
        try:

            # stage 2 reset defaults and set them from form data
            self._set_new_user_perms(
                perm_user,
                form_result,
                preserve=[
                    'default_repo_group_create', 'default_user_group_create',
                    'default_repo_create_on_write', 'default_repo_create',
                    'default_fork_create',
                    'default_inherit_default_permissions', 'default_register',
                    'default_extern_activate'
                ])

            # overwrite default repo permissions
            if form_result['overwrite_default_repo']:
                _def_name = form_result['default_repo_perm'].split(
                    'repository.')[-1]
                _def = Permission.get_by_key('repository.' + _def_name)
                for r2p in self.sa.query(UserRepoToPerm)\
                               .filter(UserRepoToPerm.user == perm_user)\
                               .all():
                    # don't reset PRIVATE repositories
                    if not r2p.repository.private:
                        r2p.permission = _def
                        self.sa.add(r2p)

            # overwrite default repo group permissions
            if form_result['overwrite_default_group']:
                _def_name = form_result['default_group_perm'].split(
                    'group.')[-1]
                _def = Permission.get_by_key('group.' + _def_name)
                for g2p in self.sa.query(UserRepoGroupToPerm)\
                               .filter(UserRepoGroupToPerm.user == perm_user)\
                               .all():
                    g2p.permission = _def
                    self.sa.add(g2p)

            # overwrite default user group permissions
            if form_result['overwrite_default_user_group']:
                _def_name = form_result['default_user_group_perm'].split(
                    'usergroup.')[-1]
                # user groups
                _def = Permission.get_by_key('usergroup.' + _def_name)
                for g2p in self.sa.query(UserUserGroupToPerm)\
                               .filter(UserUserGroupToPerm.user == perm_user)\
                               .all():
                    g2p.permission = _def
                    self.sa.add(g2p)
            self.sa.commit()
        except (DatabaseError, ):
            log.exception('Failed to set default object permissions')
            self.sa.rollback()
            raise
Пример #12
0
def pre_push(extras):
    """
    Hook executed before pushing code.

    It bans pushing when the repository is locked.
    """
    usr = User.get_by_username(extras.username)

    output = ''
    if extras.locked_by[0] and usr.user_id != int(extras.locked_by[0]):
        locked_by = User.get(extras.locked_by[0]).username
        reason = extras.locked_by[2]
        # this exception is interpreted in git/hg middlewares and based
        # on that proper return code is server to client
        _http_ret = HTTPLockedRC(
            _locked_by_explanation(extras.repository, locked_by, reason))
        if str(_http_ret.code).startswith('2'):
            # 2xx Codes don't raise exceptions
            output = _http_ret.title
        else:
            raise _http_ret

    # Calling hooks after checking the lock, for consistent behavior
    pre_push_extension(repo_store_path=Repository.base_path(), **extras)

    return HookResponse(0, output)
Пример #13
0
    def index(self):
        # Return a rendered template
        p = int(request.params.get('page', 1))

        c.user = User.get(self.rhodecode_user.user_id)
        all_repos = self.sa.query(Repository)\
                     .filter(Repository.user_id == c.user.user_id)\
                     .order_by(func.lower(Repository.repo_name)).all()

        c.user_repos = ScmModel().get_repos(all_repos)

        c.following = self.sa.query(UserFollowing)\
            .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
            .options(joinedload(UserFollowing.follows_repository))\
            .all()

        journal = self._get_journal_data(c.following)

        c.journal_pager = Page(journal, page=p, items_per_page=20)

        c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)

        c.journal_data = render('journal/journal_data.html')
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
            return c.journal_data
        return render('journal/journal.html')
Пример #14
0
def pre_push(ui, repo, **kwargs):
    # pre push function, currently used to ban pushing when
    # repository is locked
    try:
        rc_extras = json.loads(os.environ.get('RC_SCM_DATA', "{}"))
    except:
        rc_extras = {}
    extras = dict(repo.ui.configitems('rhodecode_extras'))

    if 'username' in extras:
        username = extras['username']
        repository = extras['repository']
        scm = extras['scm']
        locked_by = extras['locked_by']
    elif 'username' in rc_extras:
        username = rc_extras['username']
        repository = rc_extras['repository']
        scm = rc_extras['scm']
        locked_by = rc_extras['locked_by']
    else:
        raise Exception('Missing data in repo.ui and os.environ')

    usr = User.get_by_username(username)
    if locked_by[0] and usr.user_id != int(locked_by[0]):
        locked_by = User.get(locked_by[0]).username
        raise HTTPLockedRC(repository, locked_by)
Пример #15
0
    def index(self):
        # Return a rendered template
        p = int(request.params.get('page', 1))

        c.user = User.get(self.rhodecode_user.user_id)
        all_repos = self.sa.query(Repository)\
                     .filter(Repository.user_id == c.user.user_id)\
                     .order_by(func.lower(Repository.repo_name)).all()

        c.user_repos = ScmModel().get_repos(all_repos)

        c.following = self.sa.query(UserFollowing)\
            .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
            .options(joinedload(UserFollowing.follows_repository))\
            .all()

        journal = self._get_journal_data(c.following)

        c.journal_pager = Page(journal, page=p, items_per_page=20)

        c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)

        c.journal_data = render('journal/journal_data.html')
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
            return c.journal_data
        return render('journal/journal.html')
Пример #16
0
    def deactivate_last_users(self, expected_users):
        """
        Deactivate accounts that are over the license limits.
        Algorithm of which accounts to disabled is based on the formula:

        Get current user, then super admins in creation order, then regular
        active users in creation order.

        Using that list we mark all accounts from the end of it as inactive.
        This way we block only latest created accounts.

        :param expected_users: list of users in special order, we deactivate
            the end N ammoun of users from that list
        """

        list_of_accounts = self.get_accounts_in_creation_order()

        for acc_id in list_of_accounts[expected_users + 1:]:
            user = User.get(acc_id)
            log.info('Deactivating account %s for license unlock', user)
            user.active = False
            Session().add(user)
            Session().commit()

        return
Пример #17
0
    def my_account(self):
        """
        GET /_admin/my_account Displays info about my account
        """
        # url('admin_settings_my_account')

        c.user = User.get(self.rhodecode_user.user_id)
        c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
                               ip_addr=self.ip_addr)
        c.ldap_dn = c.user.ldap_dn

        if c.user.username == 'default':
            h.flash(_("You can't edit this user since it's"
              " crucial for entire application"), category='warning')
            return redirect(url('users'))

        #json used to render the grid
        c.data = self._load_my_repos_data()

        defaults = c.user.get_dict()

        c.form = htmlfill.render(
            render('admin/users/user_edit_my_account_form.html'),
            defaults=defaults,
            encoding="UTF-8",
            force_defaults=False
        )
        return render('admin/users/user_edit_my_account.html')
Пример #18
0
    def my_account(self):
        """
        GET /_admin/my_account Displays info about my account
        """
        # url('admin_settings_my_account')

        c.user = User.get(self.rhodecode_user.user_id)
        c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
                               ip_addr=self.ip_addr)
        c.ldap_dn = c.user.ldap_dn

        if c.user.username == 'default':
            h.flash(_("You can't edit this user since it's"
                      " crucial for entire application"),
                    category='warning')
            return redirect(url('users'))

        #json used to render the grid
        c.data = self._load_my_repos_data()

        defaults = c.user.get_dict()

        c.form = htmlfill.render(
            render('admin/users/user_edit_my_account_form.html'),
            defaults=defaults,
            encoding="UTF-8",
            force_defaults=False)
        return render('admin/users/user_edit_my_account.html')
Пример #19
0
    def my_account_update(self):
        """PUT /_admin/my_account_update: 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('admin_settings_my_account_update'),
        #           method='put')
        # url('admin_settings_my_account_update', id=ID)
        user_model = UserModel()
        uid = self.rhodecode_user.user_id
        _form = UserForm(edit=True,
                         old_data={'user_id': uid,
                                   'email': self.rhodecode_user.email})()
        form_result = {}
        try:
            form_result = _form.to_python(dict(request.POST))
            user_model.update_my_account(uid, form_result)
            h.flash(_('Your account was updated successfully'),
                    category='success')
            Session.commit()
        except formencode.Invalid, errors:
            c.user = User.get(self.rhodecode_user.user_id)
            all_repos = self.sa.query(Repository)\
                .filter(Repository.user_id == c.user.user_id)\
                .order_by(func.lower(Repository.repo_name))\
                .all()
            c.user_repos = ScmModel().get_repos(all_repos)

            return htmlfill.render(
                render('admin/users/user_edit_my_account.html'),
                defaults=errors.value,
                errors=errors.error_dict or {},
                prefix_error=False,
                encoding="UTF-8")
Пример #20
0
    def my_account(self):
        """
        GET /_admin/my_account Displays info about my account
        """
        # url('admin_settings_my_account')

        c.user = User.get(self.rhodecode_user.user_id)
        all_repos = self.sa.query(Repository)\
                     .filter(Repository.user_id == c.user.user_id)\
                     .order_by(func.lower(Repository.repo_name)).all()

        c.user_repos = ScmModel().get_repos(all_repos)

        if c.user.username == 'default':
            h.flash(_("You can't edit this user since it's"
              " crucial for entire application"), category='warning')
            return redirect(url('users'))

        defaults = c.user.get_dict()
        return htmlfill.render(
            render('admin/users/user_edit_my_account.html'),
            defaults=defaults,
            encoding="UTF-8",
            force_defaults=False
        )
Пример #21
0
def post_pull(extras):
    """Hook executed after client pulls the code."""
    user = User.get_by_username(extras.username)
    action = 'pull'
    action_logger(user, action, extras.repository, extras.ip, commit=True)

    # extension hook call
    post_pull_extension(**extras)

    output = ''
    # make lock is a tri state False, True, None. We only make lock on True
    if extras.make_lock is True:
        Repository.lock(Repository.get_by_repo_name(extras.repository),
                        user.user_id,
                        lock_reason=Repository.LOCK_PULL)
        msg = 'Made lock on repo `%s`' % (extras.repository, )
        output += msg

    if extras.locked_by[0]:
        locked_by = User.get(extras.locked_by[0]).username
        reason = extras.locked_by[2]
        _http_ret = HTTPLockedRC(
            _locked_by_explanation(extras.repository, locked_by, reason))
        if str(_http_ret.code).startswith('2'):
            # 2xx Codes don't raise exceptions
            output += _http_ret.title

    return HookResponse(0, output)
Пример #22
0
    def update_application_permissions(self, form_result):
        if 'perm_user_id' in form_result:
            perm_user = User.get(safe_int(form_result['perm_user_id']))
        else:
            # used mostly to do lookup for default user
            perm_user = User.get_by_username(form_result['perm_user_name'])

        try:
            # stage 1 set anonymous access
            if perm_user.username == User.DEFAULT_USER:
                perm_user.active = str2bool(form_result['anonymous'])
                self.sa.add(perm_user)

            # stage 2 reset defaults and set them from form data
            self._set_new_user_perms(perm_user,
                                     form_result,
                                     preserve=[
                                         'default_repo_perm',
                                         'default_group_perm',
                                         'default_user_group_perm',
                                         'default_repo_group_create',
                                         'default_user_group_create',
                                         'default_repo_create_on_write',
                                         'default_repo_create',
                                         'default_fork_create',
                                         'default_inherit_default_permissions',
                                     ])

            self.sa.commit()
        except (DatabaseError, ):
            log.error(traceback.format_exc())
            self.sa.rollback()
            raise
Пример #23
0
def log_pull_action(ui, repo, **kwargs):
    """
    Logs user last pull action

    :param ui:
    :param repo:
    """
    ex = _extract_extras()

    user = User.get_by_username(ex.username)
    action = 'pull'
    action_logger(user, action, ex.repository, ex.ip, commit=True)
    # extension hook call
    from rhodecode import EXTENSIONS
    callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
    if isfunction(callback):
        kw = {}
        kw.update(ex)
        callback(**kw)

    if ex.make_lock is not None and ex.make_lock:
        Repository.lock(Repository.get_by_repo_name(ex.repository), user.user_id)
        #msg = 'Made lock on repo `%s`' % repository
        #sys.stdout.write(msg)

    if ex.locked_by[0]:
        locked_by = User.get(ex.locked_by[0]).username
        _http_ret = HTTPLockedRC(ex.repository, locked_by)
        if str(_http_ret.code).startswith('2'):
            #2xx Codes don't raise exceptions
            sys.stdout.write(_http_ret.title)
    return 0
Пример #24
0
def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
    """
    Action logger for various actions made by users

    :param user: user that made this action, can be a unique username string or
        object containing user_id attribute
    :param action: action to log, should be on of predefined unique actions for
        easy translations
    :param repo: string name of repository or object containing repo_id,
        that action was made on
    :param ipaddr: optional ip address from what the action was made
    :param sa: optional sqlalchemy session

    """

    if not sa:
        sa = meta.Session()
    # if we don't get explicit IP address try to get one from registered user
    # in tmpl context var
    if not ipaddr:
        ipaddr = getattr(get_current_rhodecode_user(), 'ip_addr', '')

    try:
        if hasattr(user, 'user_id'):
            user_obj = User.get(user.user_id)
        elif isinstance(user, basestring):
            user_obj = User.get_by_username(user)
        else:
            raise Exception('You have to provide a user object or a username')

        if hasattr(repo, 'repo_id'):
            repo_obj = Repository.get(repo.repo_id)
            repo_name = repo_obj.repo_name
        elif isinstance(repo, basestring):
            repo_name = repo.lstrip('/')
            repo_obj = Repository.get_by_repo_name(repo_name)
        else:
            repo_obj = None
            repo_name = ''

        user_log = UserLog()
        user_log.user_id = user_obj.user_id
        user_log.username = user_obj.username
        user_log.action = safe_unicode(action)

        user_log.repository = repo_obj
        user_log.repository_name = repo_name

        user_log.action_date = datetime.datetime.now()
        user_log.user_ip = ipaddr
        sa.add(user_log)

        log.info('Logging action:%s on %s by user:%s ip:%s' %
                 (action, safe_unicode(repo), user_obj, ipaddr))
        if commit:
            sa.commit()
    except Exception:
        log.error(traceback.format_exc())
        raise
Пример #25
0
def log_push_action(ui, repo, **kwargs):
    """
    Maps user last push action to new changeset id, from mercurial

    :param ui:
    :param repo: repo object containing the `ui` object
    """

    ex = _extract_extras()

    action = ex.action + ':%s'

    if ex.scm == 'hg':
        node = kwargs['node']

        def get_revs(repo, rev_opt):
            if rev_opt:
                revs = revrange(repo, rev_opt)

                if len(revs) == 0:
                    return (nullrev, nullrev)
                return (max(revs), min(revs))
            else:
                return (len(repo) - 1, 0)

        stop, start = get_revs(repo, [node + ':'])
        h = binascii.hexlify
        revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
    elif ex.scm == 'git':
        revs = kwargs.get('_git_revs', [])
        if '_git_revs' in kwargs:
            kwargs.pop('_git_revs')

    action = action % ','.join(revs)

    action_logger(ex.username, action, ex.repository, ex.ip, commit=True)

    # extension hook call
    from rhodecode import EXTENSIONS
    callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
    if isfunction(callback):
        kw = {'pushed_revs': revs}
        kw.update(ex)
        callback(**kw)

    if ex.make_lock is not None and not ex.make_lock:
        Repository.unlock(Repository.get_by_repo_name(ex.repository))
        msg = 'Released lock on repo `%s`\n' % ex.repository
        sys.stdout.write(msg)

    if ex.locked_by[0]:
        locked_by = User.get(ex.locked_by[0]).username
        _http_ret = HTTPLockedRC(ex.repository, locked_by)
        if str(_http_ret.code).startswith('2'):
            #2xx Codes don't raise exceptions
            sys.stdout.write(_http_ret.title)

    return 0
Пример #26
0
def log_push_action(ui, repo, **kwargs):
    """
    Maps user last push action to new changeset id, from mercurial

    :param ui:
    :param repo: repo object containing the `ui` object
    """

    ex = _extract_extras()

    action = ex.action + ':%s'

    if ex.scm == 'hg':
        node = kwargs['node']

        def get_revs(repo, rev_opt):
            if rev_opt:
                revs = revrange(repo, rev_opt)

                if len(revs) == 0:
                    return (nullrev, nullrev)
                return (max(revs), min(revs))
            else:
                return (len(repo) - 1, 0)

        stop, start = get_revs(repo, [node + ':'])
        h = binascii.hexlify
        revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
    elif ex.scm == 'git':
        revs = kwargs.get('_git_revs', [])
        if '_git_revs' in kwargs:
            kwargs.pop('_git_revs')

    action = action % ','.join(revs)

    action_logger(ex.username, action, ex.repository, ex.ip, commit=True)

    # extension hook call
    from rhodecode import EXTENSIONS
    callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
    if isfunction(callback):
        kw = {'pushed_revs': revs}
        kw.update(ex)
        callback(**kw)

    if ex.make_lock is not None and not ex.make_lock:
        Repository.unlock(Repository.get_by_repo_name(ex.repository))
        msg = 'Released lock on repo `%s`\n' % ex.repository
        sys.stdout.write(msg)

    if ex.locked_by[0]:
        locked_by = User.get(ex.locked_by[0]).username
        _http_ret = HTTPLockedRC(ex.repository, locked_by)
        if str(_http_ret.code).startswith('2'):
            #2xx Codes don't raise exceptions
            sys.stdout.write(_http_ret.title)

    return 0
Пример #27
0
    def test_delete(self, user, password):
        self.log_user(user, password)
        cur_user = self._get_logged_user()

        u1 = UserModel().create_or_update(username='******',
                                          password='******',
                                          email='*****@*****.**',
                                          firstname='u1',
                                          lastname='u1')
        u2 = UserModel().create_or_update(username='******',
                                          password='******',
                                          email='*****@*****.**',
                                          firstname='u2',
                                          lastname='u2')
        self.destroy_users.add('u1')
        self.destroy_users.add('u2')

        # make notifications
        notification = NotificationModel().create(
            created_by=cur_user,
            notification_subject=u'test',
            notification_body=u'hi there',
            recipients=[cur_user, u1, u2])
        Session().commit()
        u1 = User.get(u1.user_id)
        u2 = User.get(u2.user_id)

        # check DB
        get_notif = lambda un: [x.notification for x in un]
        assert get_notif(cur_user.notifications) == [notification]
        assert get_notif(u1.notifications) == [notification]
        assert get_notif(u2.notifications) == [notification]
        cur_usr_id = cur_user.user_id

        response = self.app.post(url(
            'notification', notification_id=notification.notification_id),
                                 params={
                                     '_method': 'delete',
                                     'csrf_token': self.csrf_token
                                 })
        assert response.body == 'ok'

        cur_user = User.get(cur_usr_id)
        assert cur_user.notifications == []
 def test_my_account_add_auth_tokens(self, desc, lifetime):
     usr = self.log_user('test_regular2', 'test12')
     user = User.get(usr['user_id'])
     response = self.app.post(
         url('my_account_auth_tokens'), {
             'description': desc,
             'lifetime': lifetime,
             'csrf_token': self.csrf_token
         })
     assert_session_flash(response, 'Auth token successfully created')
     try:
         response = response.follow()
         user = User.get(usr['user_id'])
         for auth_token in user.auth_tokens:
             response.mustcontain(auth_token)
     finally:
         for auth_token in UserApiKeys.query().all():
             Session().delete(auth_token)
             Session().commit()
Пример #29
0
def person_by_id(id_, show_attr="username_and_name"):
    # attr to return from fetched user
    person_getter = lambda usr: getattr(usr, show_attr)

    #maybe it's an ID ?
    if str(id_).isdigit() or isinstance(id_, int):
        id_ = int(id_)
        user = User.get(id_)
        if user is not None:
            return person_getter(user)
    return id_
Пример #30
0
def person_by_id(id_, show_attr="username_and_name"):
    # attr to return from fetched user
    person_getter = lambda usr: getattr(usr, show_attr)

    #maybe it's an ID ?
    if str(id_).isdigit() or isinstance(id_, int):
        id_ = int(id_)
        user = User.get(id_)
        if user is not None:
            return person_getter(user)
    return id_
Пример #31
0
    def test_create_notification(self):
        usrs = [self.u1, self.u2]
        notification = NotificationModel().create(
            created_by=self.u1,
            notification_subject=u'subj',
            notification_body=u'hi there',
            recipients=usrs)
        Session().commit()
        u1 = User.get(self.u1)
        u2 = User.get(self.u2)
        notifications = Notification.query().all()
        assert len(notifications) == 1

        assert notifications[0].recipients, [u1 == u2]
        assert notification.notification_id == notifications[0].notification_id

        unotification = UserNotification.query()\
            .filter(UserNotification.notification == notification).all()

        assert len(unotification) == len(usrs)
        assert set([x.user.user_id for x in unotification]) == set(usrs)
Пример #32
0
def _destroy_project_tree(test_u1_id):
    Session.remove()
    repos_group = RepoGroup.get_by_group_name(group_name='g0')
    for el in reversed(repos_group.recursive_groups_and_repos()):
        if isinstance(el, Repository):
            RepoModel().delete(el)
        elif isinstance(el, RepoGroup):
            ReposGroupModel().delete(el, force_delete=True)

    u = User.get(test_u1_id)
    Session().delete(u)
    Session().commit()
Пример #33
0
def _destroy_project_tree(test_u1_id):
    Session.remove()
    repos_group = RepoGroup.get_by_group_name(group_name='g0')
    for el in reversed(repos_group.recursive_groups_and_repos()):
        if isinstance(el, Repository):
            RepoModel().delete(el)
        elif isinstance(el, RepoGroup):
            ReposGroupModel().delete(el, force_delete=True)

    u = User.get(test_u1_id)
    Session().delete(u)
    Session().commit()
Пример #34
0
def pre_pull(ui, repo, **kwargs):
    # pre push function, currently used to ban pushing when
    # repository is locked
    ex = _extract_extras()
    if ex.locked_by[0]:
        locked_by = User.get(ex.locked_by[0]).username
        # this exception is interpreted in git/hg middlewares and based
        # on that proper return code is server to client
        _http_ret = HTTPLockedRC(ex.repository, locked_by)
        if str(_http_ret.code).startswith('2'):
            #2xx Codes don't raise exceptions
            sys.stdout.write(_http_ret.title)
        else:
            raise _http_ret
Пример #35
0
def pre_pull(ui, repo, **kwargs):
    # pre push function, currently used to ban pushing when
    # repository is locked
    ex = _extract_extras()
    if ex.locked_by[0]:
        locked_by = User.get(ex.locked_by[0]).username
        # this exception is interpreted in git/hg middlewares and based
        # on that proper return code is server to client
        _http_ret = HTTPLockedRC(ex.repository, locked_by)
        if str(_http_ret.code).startswith('2'):
            #2xx Codes don't raise exceptions
            sys.stdout.write(_http_ret.title)
        else:
            raise _http_ret
Пример #36
0
    def my_account_auth_tokens_delete(self):
        auth_token = request.POST.get('del_auth_token')
        user_id = c.rhodecode_user.user_id
        if request.POST.get('del_auth_token_builtin'):
            user = User.get(user_id)
            if user:
                user.api_key = generate_auth_token(user.username)
                Session().add(user)
                Session().commit()
                h.flash(_("Auth token successfully reset"), category='success')
        elif auth_token:
            AuthTokenModel().delete(auth_token, c.rhodecode_user.user_id)
            Session().commit()
            h.flash(_("Auth token successfully deleted"), category='success')

        return redirect(url('my_account_auth_tokens'))
Пример #37
0
        def validate_python(self, value, state):
            if value in ["default", "new_user"]:
                msg = M(self, "system_invalid_username", state, username=value)
                raise formencode.Invalid(msg, value, state)
            # check if user is unique
            old_un = None
            if edit:
                old_un = User.get(old_data.get("user_id")).username

            if old_un != value or not edit:
                if User.get_by_username(value, case_insensitive=True):
                    msg = M(self, "username_exists", state, username=value)
                    raise formencode.Invalid(msg, value, state)

            if re.match(r"^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$", value) is None:
                msg = M(self, "invalid_username", state)
                raise formencode.Invalid(msg, value, state)
Пример #38
0
        def validate_python(self, value, state):
            if value in ['default', 'new_user']:
                msg = M(self, 'system_invalid_username', state, username=value)
                raise formencode.Invalid(msg, value, state)
            #check if user is unique
            old_un = None
            if edit:
                old_un = User.get(old_data.get('user_id')).username

            if old_un != value or not edit:
                if User.get_by_username(value, case_insensitive=True):
                    msg = M(self, 'username_exists', state, username=value)
                    raise formencode.Invalid(msg, value, state)

            if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*$', value) is None:
                msg = M(self, 'invalid_username', state)
                raise formencode.Invalid(msg, value, state)
    def test_my_account_reset_main_auth_token(self):
        usr = self.log_user('test_regular2', 'test12')
        user = User.get(usr['user_id'])
        api_key = user.api_key
        response = self.app.get(url('my_account_auth_tokens'))
        response.mustcontain(api_key)
        response.mustcontain('expires: never')

        response = self.app.post(
            url('my_account_auth_tokens'), {
                '_method': 'delete',
                'del_auth_token_builtin': api_key,
                'csrf_token': self.csrf_token
            })
        assert_session_flash(response, 'Auth token successfully reset')
        response = response.follow()
        response.mustcontain(no=[api_key])
Пример #40
0
    def _check_locking_state(self, environ, action, repo, user_id):
        """
        Checks locking on this repository, if locking is enabled and lock is
        present returns a tuple of make_lock, locked, locked_by.
        make_lock can have 3 states None (do nothing) True, make lock
        False release lock, This value is later propagated to hooks, which
        do the locking. Think about this as signals passed to hooks what to do.

        """
        locked = False  # defines that locked error should be thrown to user
        make_lock = None
        repo = Repository.get_by_repo_name(repo)
        user = User.get(user_id)

        # this is kind of hacky, but due to how mercurial handles client-server
        # server see all operation on changeset; bookmarks, phases and
        # obsolescence marker in different transaction, we don't want to check
        # locking on those
        obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
        locked_by = repo.locked
        if repo and repo.enable_locking and not obsolete_call:
            if action == 'push':
                #check if it's already locked !, if it is compare users
                user_id, _date = repo.locked
                if user.user_id == user_id:
                    log.debug('Got push from user %s, now unlocking' % (user))
                    # unlock if we have push from user who locked
                    make_lock = False
                else:
                    # we're not the same user who locked, ban with 423 !
                    locked = True
            if action == 'pull':
                if repo.locked[0] and repo.locked[1]:
                    locked = True
                else:
                    log.debug('Setting lock on repo %s by %s' % (repo, user))
                    make_lock = True

        else:
            log.debug('Repository %s do not have locking enabled' % (repo))
        log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
                  % (make_lock, locked, locked_by))
        return make_lock, locked, locked_by
Пример #41
0
    def _check_locking_state(self, environ, action, repo, user_id):
        """
        Checks locking on this repository, if locking is enabled and lock is
        present returns a tuple of make_lock, locked, locked_by.
        make_lock can have 3 states None (do nothing) True, make lock
        False release lock, This value is later propagated to hooks, which
        do the locking. Think about this as signals passed to hooks what to do.

        """
        locked = False  # defines that locked error should be thrown to user
        make_lock = None
        repo = Repository.get_by_repo_name(repo)
        user = User.get(user_id)

        # this is kind of hacky, but due to how mercurial handles client-server
        # server see all operation on changeset; bookmarks, phases and
        # obsolescence marker in different transaction, we don't want to check
        # locking on those
        obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
        locked_by = repo.locked
        if repo and repo.enable_locking and not obsolete_call:
            if action == 'push':
                #check if it's already locked !, if it is compare users
                user_id, _date = repo.locked
                if user.user_id == user_id:
                    log.debug('Got push from user %s, now unlocking' % (user))
                    # unlock if we have push from user who locked
                    make_lock = False
                else:
                    # we're not the same user who locked, ban with 423 !
                    locked = True
            if action == 'pull':
                if repo.locked[0] and repo.locked[1]:
                    locked = True
                else:
                    log.debug('Setting lock on repo %s by %s' % (repo, user))
                    make_lock = True

        else:
            log.debug('Repository %s do not have locking enabled' % (repo))
        log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
                  % (make_lock, locked, locked_by))
        return make_lock, locked, locked_by
Пример #42
0
        def validate_python(self, value, state):
            if value in ['default', 'new_user']:
                raise formencode.Invalid(_('Invalid username'), value, state)
            #check if user is unique
            old_un = None
            if edit:
                old_un = User.get(old_data.get('user_id')).username

            if old_un != value or not edit:
                if User.get_by_username(value, case_insensitive=True):
                    raise formencode.Invalid(_('This username already '
                                               'exists') , value, state)

            if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
                raise formencode.Invalid(
                    _('Username may only contain alphanumeric characters '
                      'underscores, periods or dashes and must begin with '
                      'alphanumeric character'),
                    value,
                    state
                )
Пример #43
0
    def delete_auth_token(self, user_id):
        user_id = safe_int(user_id)
        c.user = User.get_or_404(user_id)
        if c.user.username == User.DEFAULT_USER:
            h.flash(_("You can't edit this user"), category='warning')
            return redirect(url('users'))

        auth_token = request.POST.get('del_auth_token')
        if request.POST.get('del_auth_token_builtin'):
            user = User.get(c.user.user_id)
            if user:
                user.api_key = generate_auth_token(user.username)
                Session().add(user)
                Session().commit()
                h.flash(_("Auth token successfully reset"), category='success')
        elif auth_token:
            AuthTokenModel().delete(auth_token, c.user.user_id)
            Session().commit()
            h.flash(_("Auth token successfully deleted"), category='success')

        return redirect(url('edit_user_auth_tokens', user_id=c.user.user_id))
Пример #44
0
    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(id)
        if not c.user:
            return redirect(url('users'))
        if c.user.username == 'default':
            h.flash(_("You can't edit this user"), category='warning')
            return redirect(url('users'))
        c.user.permissions = {}
        c.granted_permissions = UserModel().fill_perms(c.user)\
            .permissions['global']

        defaults = c.user.get_dict()
        perm = Permission.get_by_key('hg.create.repository')
        defaults.update({'create_repo_perm': UserModel().has_perm(id, perm)})

        return htmlfill.render(render('admin/users/user_edit.html'),
                               defaults=defaults,
                               encoding="UTF-8",
                               force_defaults=False)
Пример #45
0
 def my_account_update(self):
     """PUT /_admin/my_account_update: 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('admin_settings_my_account_update'),
     #           method='put')
     # url('admin_settings_my_account_update', id=ID)
     uid = self.rhodecode_user.user_id
     c.user = User.get(self.rhodecode_user.user_id)
     c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
                            ip_addr=self.ip_addr)
     c.ldap_dn = c.user.ldap_dn
     email = self.rhodecode_user.email
     _form = UserForm(edit=True, old_data={
         'user_id': uid,
         'email': email
     })()
     form_result = {}
     try:
         form_result = _form.to_python(dict(request.POST))
         skip_attrs = ['admin', 'active']  # skip attr for my account
         if c.ldap_dn:
             #forbid updating username for ldap accounts
             skip_attrs.append('username')
         UserModel().update(uid, form_result, skip_attrs=skip_attrs)
         h.flash(_('Your account was updated successfully'),
                 category='success')
         Session().commit()
     except formencode.Invalid, errors:
         #json used to render the grid
         c.data = self._load_my_repos_data()
         c.form = htmlfill.render(
             render('admin/users/user_edit_my_account_form.html'),
             defaults=errors.value,
             errors=errors.error_dict or {},
             prefix_error=False,
             encoding="UTF-8")
         return render('admin/users/user_edit_my_account.html')
Пример #46
0
 def update_user_permissions(self, form_result):
     if 'perm_user_id' in form_result:
         perm_user = User.get(safe_int(form_result['perm_user_id']))
     else:
         # used mostly to do lookup for default user
         perm_user = User.get_by_username(form_result['perm_user_name'])
     try:
         # stage 2 reset defaults and set them from form data
         self._set_new_user_perms(perm_user,
                                  form_result,
                                  preserve=[
                                      'default_repo_perm',
                                      'default_group_perm',
                                      'default_user_group_perm',
                                      'default_register',
                                      'default_extern_activate'
                                  ])
         self.sa.commit()
     except (DatabaseError, ):
         log.error(traceback.format_exc())
         self.sa.rollback()
         raise
Пример #47
0
    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(id)
        if not c.user:
            return redirect(url('users'))
        if c.user.username == 'default':
            h.flash(_("You can't edit this user"), category='warning')
            return redirect(url('users'))
        c.user.permissions = {}
        c.granted_permissions = UserModel().fill_perms(c.user)\
            .permissions['global']

        defaults = c.user.get_dict()
        perm = Permission.get_by_key('hg.create.repository')
        defaults.update({'create_repo_perm': UserModel().has_perm(id, perm)})

        return htmlfill.render(
            render('admin/users/user_edit.html'),
            defaults=defaults,
            encoding="UTF-8",
            force_defaults=False
        )
Пример #48
0
 def my_account_update(self):
     """PUT /_admin/my_account_update: 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('admin_settings_my_account_update'),
     #           method='put')
     # url('admin_settings_my_account_update', id=ID)
     uid = self.rhodecode_user.user_id
     c.user = User.get(self.rhodecode_user.user_id)
     c.perm_user = AuthUser(user_id=self.rhodecode_user.user_id,
                            ip_addr=self.ip_addr)
     c.ldap_dn = c.user.ldap_dn
     email = self.rhodecode_user.email
     _form = UserForm(edit=True,
                      old_data={'user_id': uid, 'email': email})()
     form_result = {}
     try:
         form_result = _form.to_python(dict(request.POST))
         skip_attrs = ['admin', 'active']  # skip attr for my account
         if c.ldap_dn:
             #forbid updating username for ldap accounts
             skip_attrs.append('username')
         UserModel().update(uid, form_result, skip_attrs=skip_attrs)
         h.flash(_('Your account was updated successfully'),
                 category='success')
         Session().commit()
     except formencode.Invalid, errors:
         #json used to render the grid
         c.data = self._load_my_repos_data()
         c.form = htmlfill.render(
             render('admin/users/user_edit_my_account_form.html'),
             defaults=errors.value,
             errors=errors.error_dict or {},
             prefix_error=False,
             encoding="UTF-8")
         return render('admin/users/user_edit_my_account.html')
Пример #49
0
    def index(self):
        # Return a rendered template
        p = safe_int(request.GET.get('page', 1), 1)
        c.user = User.get(self.rhodecode_user.user_id)
        c.following = self.sa.query(UserFollowing)\
            .filter(UserFollowing.user_id == self.rhodecode_user.user_id)\
            .options(joinedload(UserFollowing.follows_repository))\
            .all()

        journal = self._get_journal_data(c.following)

        def url_generator(**kw):
            return url.current(filter=c.search_term, **kw)

        c.journal_pager = Page(journal, page=p, items_per_page=20, url=url_generator)
        c.journal_day_aggreagate = self._get_daily_aggregate(c.journal_pager)

        c.journal_data = render('journal/journal_data.html')
        if request.environ.get('HTTP_X_PARTIAL_XHR'):
            return c.journal_data

        repos_list = Session().query(Repository)\
                     .filter(Repository.user_id ==
                             self.rhodecode_user.user_id)\
                     .order_by(func.lower(Repository.repo_name)).all()

        repos_data = RepoModel().get_repos_as_dict(repos_list=repos_list,
                                                   admin=True)
        #json used to render the grid
        c.data = json.dumps(repos_data)

        watched_repos_data = []

        ## watched repos
        _render = RepoModel._render_datatable

        def quick_menu(repo_name):
            return _render('quick_menu', repo_name)

        def repo_lnk(name, rtype, private, fork_of):
            return _render('repo_name', name, rtype, private, fork_of,
                           short_name=False, admin=False)

        def last_rev(repo_name, cs_cache):
            return _render('revision', repo_name, cs_cache.get('revision'),
                           cs_cache.get('raw_id'), cs_cache.get('author'),
                           cs_cache.get('message'))

        def desc(desc):
            from pylons import tmpl_context as c
            if c.visual.stylify_metatags:
                return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
            else:
                return h.urlify_text(h.truncate(desc, 60))

        def repo_actions(repo_name):
            return _render('repo_actions', repo_name)

        def owner_actions(user_id, username):
            return _render('user_name', user_id, username)

        def toogle_follow(repo_id):
            return  _render('toggle_follow', repo_id)

        for entry in c.following:
            repo = entry.follows_repository
            cs_cache = repo.changeset_cache
            row = {
                "menu": quick_menu(repo.repo_name),
                "raw_name": repo.repo_name.lower(),
                "name": repo_lnk(repo.repo_name, repo.repo_type,
                                 repo.private, repo.fork),
                "last_changeset": last_rev(repo.repo_name, cs_cache),
                "raw_tip": cs_cache.get('revision'),
                "action": toogle_follow(repo.repo_id)
            }

            watched_repos_data.append(row)

        c.watched_data = json.dumps({
            "totalRecords": len(c.following),
            "startIndex": 0,
            "sort": "name",
            "dir": "asc",
            "records": watched_repos_data
        })
        return render('journal/journal.html')