Пример #1
0
    def filter_events(self, events, user, project):
        """ Filter event list based on user's permissions
        """
        filtered_events = []
        policy = CQDEPermissionPolicy(self.env)
        permission_map = {
            'newticket': 'TICKET_VIEW',
            'closedticket': 'TICKET_VIEW',
            'reopenedticket': 'TICKET_VIEW',
            'changeset': 'CHANGESET_VIEW',
            'wiki': 'WIKI_VIEW',
            'attachment': 'ATTACHMENT_VIEW',
            'newmessage': 'DISCUSSION_VIEW',
            'newtopic': 'DISCUSSION_VIEW',
            'newforum': 'DISCUSSION_VIEW',
            'milestone': 'MILESTONE_VIEW'
        }

        for event in events:
            event_type = event[1]['kind']
            perm = permission_map.get(event_type)
            if perm and policy.check_permission(project.trac_environment_key, perm, user.username):
                filtered_events.append(event)

        return filtered_events
Пример #2
0
    def filter_events(self, events, user, project):
        """ Filter event list based on user's permissions
        """
        filtered_events = []
        policy = CQDEPermissionPolicy(self.env)
        permission_map = {
            'newticket': 'TICKET_VIEW',
            'closedticket': 'TICKET_VIEW',
            'reopenedticket': 'TICKET_VIEW',
            'changeset': 'CHANGESET_VIEW',
            'wiki': 'WIKI_VIEW',
            'attachment': 'ATTACHMENT_VIEW',
            'newmessage': 'DISCUSSION_VIEW',
            'newtopic': 'DISCUSSION_VIEW',
            'newforum': 'DISCUSSION_VIEW',
            'milestone': 'MILESTONE_VIEW'
        }

        for event in events:
            event_type = event[1]['kind']
            perm = permission_map.get(event_type)
            if perm and policy.check_permission(project.trac_environment_key,
                                                perm, user.username):
                filtered_events.append(event)

        return filtered_events
Пример #3
0
    def process_request(self, req):
        """
        Render welcome page
        """
        # Cast into bool directly, since match object properties are not needed
        viewing_user_profile = bool(RE_HOME_USER.match(req.path_info))
        userstore = get_userstore()

        if (req.authname == 'anonymous'
            and not self.env.config.getbool('multiproject', 'allow_public_projects')):
            conf.redirect(req, req.href('/user'))

        if viewing_user_profile:
            username = req.path_info.rsplit("/")[-1]
        else:
            username = req.authname

        user = userstore.getUser(username)
        if not user:
            raise TracError("User not found.")

        if req.authname == 'anonymous' and not viewing_user_profile:
            conf.redirect(req, conf.url_home_path + '/user')

        if user.username == 'anonymous' or user.username == 'authenticated':
            raise TracError("User not found.")

        # Possible values
        sort_cols = {'DATE': 5, 'PRIORITY': 6, 'PROJECT': 7}
        desc_asc = {'DESC': True, 'ASC': False}

        sort_tasks_by = req.args.get('sort_tasks_by','DATE')
        if sort_tasks_by not in sort_cols:
            sort_tasks_by = 'DATE'
        sort_col = sort_cols[sort_tasks_by]

        sort_options = {'DESC':'DESC', 'ASC':'ASC'}
        sort_tasks_order = sort_options.get(req.args.get('sort_tasks_order'), 'ASC')


        data = {}
        data['username'] = user.getDisplayName()
        if viewing_user_profile:
            if username != req.authname:
                # TODO: i18n support
                data['usernames'] = username + "'s"
            else:
                data['usernames'] = "Your"
        else:
            data['username'] = "******".format(user.givenName, user.lastName)
            data['usernames'] = "My"

        data['baseurl'] = conf.url_projects_path
        data['userpage'] = viewing_user_profile
        data['base_path'] = req.base_path

        # Prepare data for template
        prjs = Projects()

        default_projects, default_names = prjs.get_default_projects()
        data['default_projects'] = default_projects
        try:
            if viewing_user_profile:
                projects = prjs.get_participated_projects(user, by_ldap=False, public_only=True)
            else:
                projects = prjs.get_participated_projects(user, by_ldap=True, public_only=False)
        except TracError as e:
            projects = []

        if viewing_user_profile:
            all_projects = projects
        else:
            all_projects = default_projects + [p for p in projects if p.env_name not in default_names]

        admin_projects = []
        other_projects = []

        if viewing_user_profile:
            other_projects = all_projects
        else:
            for project in all_projects:
                if project.is_admin(user.username):
                    admin_projects.append(project)
                else:
                    other_projects.append(project)

        # admin_projects is [] in public profile or if there are not public, administrated projects
        data['projects_where_admin'] = admin_projects
        data['projects'] = other_projects

        # Get tickets and posts
        # [project, row[URL], row[SUMMARY], row[DESCRIPTION], row[PRIORITY], to_datetime(row[TIME]/1000000)]
        policy = CQDEPermissionPolicy(self.env)
        ticket_projects = []
        post_projects = []
        if viewing_user_profile:
            for project in all_projects:
                self.log.warning('project %s' % project.env_name)
                if policy.check_permission(project.env_name, 'TICKET_VIEW', 'anonymous'):
                    ticket_projects.append(project)
                if policy.check_permission(project.env_name, 'DISCUSSION_VIEW', 'anonymous'):
                    post_projects.append(project)
        else:
            ticket_projects = all_projects
            post_projects = all_projects

        tasks = prjs.get_all_user_tasks(user.username, ticket_projects)

        do_reverse = desc_asc[sort_tasks_order]
        tasks = sorted(tasks, key = lambda task: task[sort_col], reverse = do_reverse)

        # Get posts
        posts = self._get_posts(user.username, post_projects)

        data['tasks'] = tasks

        (totaltickets, totalclosed) = prjs.get_all_user_task_sums(user.username, all_projects)

        data['user'] = user
        data['known_priorities'] = ['blocker', 'critical', 'major', 'minor', 'trivial']
        data['sort_tasks_by'] = sort_tasks_by
        data['sort_tasks_order'] = sort_tasks_order
        data['posts'] = posts[:10]
        data['userpage'] = viewing_user_profile
        data['to_web_time'] = to_web_time

        # Check if user can create a project
        data['can_create_project'] = user.can_create_project()

        if viewing_user_profile:
            if username != req.authname:
                data['title'] = username + "'s profile"
                data['badgelinktitle'] = "View profile"
            else:
                data['title'] = "This is your public profile"
                data['badgelinktitle'] = "View your profile"
        else:
            data['title'] = "My projects"
            data['badgelinktitle'] = "View my public profile"

        topics_started = 0
        for post in posts:
            if post['topic_id'] == 0:
                topics_started += 1

        data['user'].details = {'Total tickets':totaltickets,
                                'Total tickets closed':totalclosed,
                                'Discussions started': topics_started}
        if not viewing_user_profile:
            data['watchlist'] = self._get_watchlist_events(user)

        return "myprojects.html", data, None
class ProjectUserVisibilityGenerator():
    def __init__(self, verbose=False):
        """
        If verbose = None, be absolutely quiet
        """
        self.verbose = verbose
        self.policy = CQDEPermissionPolicy(MockEnvironment())
        self.papi = Projects()
        self.batch_size = conf.visibility_db_batch_size
        self.required_permission = 'PROJECT_VIEW'

    def get_anonymous_user_id(self):
        anon = get_userstore().getUser('anonymous')
        if anon:
            anon_id = safe_int(anon.id)
        else:
            anon_id = None

        return anon_id

    def get_public_project_pairs(self, anon_id):
        """
        :param anon_id: Anonymous user_id
        :return: List of tuples (project_id, trac_environment_key)
        """
        # TODO: it's not clearly defined when project is public
        perms = ['PROJECT_VIEW', 'TRAC_ADMIN']
        perm_ids = [get_permission_id(perm) for perm in perms]

        query = """SELECT DISTINCT p.project_id, p.trac_environment_key
                          FROM projects p
                    INNER JOIN `group` g ON g.trac_environment_key = p.trac_environment_key
                    INNER JOIN user_group ON user_group.group_key = g.group_id
                    INNER JOIN group_permission ON group_permission.group_key = g.group_id
                         WHERE user_group.user_key = {0}
                           AND group_permission.permission_key IN ({1})
                          """.format(anon_id, ', '.join([str(int(id)) for id in perm_ids]))

        id_pairs = []
        with admin_query() as cursor:
            try:
                cursor.execute(query)
                for row in cursor:
                    id_pair = (int(row[0]), int(row[1]))
                    id_pairs.append(id_pair)
            except Exception as e:
                if self.verbose is not None:
                    print "Exception. In method get_public_project_pairs, the following query failed."
                    print query
                    print e
                conf.log.exception("Exception. ProjectUserVisibilityGenerator.get_public_project_pairs "
                                   "failed with query '''%s'''." % query)

        return id_pairs

    def get_private_project_pairs(self, public_id_pairs = None):
        query = "SELECT project_id, trac_environment_key FROM projects "

        if public_id_pairs:
            public_project_ids = [pair[0] for pair in public_id_pairs]
            # TODO: What if there are really many public projects?
            pub_projs = ','.join(str(id) for id in public_project_ids)
            query += "WHERE projects.project_id NOT IN (%s)" % pub_projs

        id_pairs = []
        with admin_query() as cursor:
            try:
                cursor.execute(query)
                for row in cursor:
                    id_pair = (int(row[0]), int(row[1]))
                    id_pairs.append(id_pair)
            except Exception as e:
                if self.verbose is not None:
                    print "Exception. In method get_private_project_pairs, the following query failed."
                    print query
                    print e
                conf.log.exception("Exception. ProjectUserVisibilityGenerator.get_private_project_pairs failed "
                                   "with query '''%s'''." % query)

        return id_pairs

    def get_all_users(self, except_anon=True):
        """
        Returns :py:class:`User` objects for every user except 'authenticated' and, if
        anon_id is given, 'anonymous'.

        No row for 'authenticated' users are generated currently, this is why the 'authenticated'
        user is left out.

        :param bool except_anon: when True, don't include anon user
        :returns: A list of :py:class:`User` objects
        """
        if except_anon:
            query = """SELECT user_id, username FROM `user`
                        WHERE username NOT IN ('anonymous', 'authenticated') """
        else:
            query = """SELECT user_id, username FROM `user`
                        WHERE username NOT IN ('authenticated') """

        users = []
        with admin_query() as cursor:
            try:
                cursor.execute(query)
                for row in cursor:
                    user = User.from_sql_row(row)
                    users.append(user)
            except Exception as e:
                if self.verbose is not None:
                    print "Exception. In method get_all_users, the following query failed."
                    print query
                    print e
                conf.log.exception("Exception. ProjectUserVisibilityGenerator.get_all_users failed "
                                   "with query '''%s'''." % query)

        return users

    def user_can_view_project(self, trac_environment_key, username):
        return self.policy.check_permission(trac_environment_key, self.required_permission, username)

    def clear_project_visibilities(self, project_id):
        query = "DELETE FROM project_user_visibility WHERE project_id = %s"

        with admin_transaction() as cursor:
            try:
                cursor.execute(query, project_id)
            except Exception as e:
                if self.verbose is not None:
                    print "Exception. In method clear_visibilities, the following query failed."
                    print query
                    print e
                conf.log.exception("Exception. ProjectUserVisibilityGenerator.clear_visibilities "
                                   "failed with query '''%s'''." % query)

    def batch_insert(self, visibilities):
        query = "INSERT INTO project_user_visibility (project_id, user_id) VALUES "

        query += ",".join(["(%d,%d)" % (safe_int(visibility.project_id),
                                        safe_int(visibility.user_id))
                           for visibility in visibilities])

        with admin_transaction() as cursor:
            try:
                cursor.execute(query)
            except Exception as e:
                if self.verbose is not None:
                    print "Exception. In method batch_insert, the following query failed."
                    print query
                    print e
                conf.log.exception("Exception. ProjectUserVisibilityGenerator.batch_insert '''%s'''." % query)

    def flush_buffer(self):
        if len(self.buffer) > 0:
            self.batch_insert(self.buffer)
            self.buffer = []

    def insert_visibilities(self, buffer):
        project_visibilities = []
        project_id = buffer[0].project_id
        for visibility in buffer:
            if project_id == visibility.project_id:
                project_visibilities.append(visibility)
            else:
                self.clear_project_visibilities(project_visibilities[0].project_id)
                self.buffered_insert(project_visibilities)
                project_visibilities = [visibility]
                project_id = visibility.project_id

        if project_visibilities:
            self.clear_project_visibilities(project_visibilities[0].project_id)
            self.buffered_insert(project_visibilities)

    def buffered_insert(self, buffer):
        while len(buffer) >= self.batch_size:
            self.batch_insert(buffer[:self.batch_size])
            buffer = buffer[self.batch_size:]

        if len(buffer) > 0:
            self.batch_insert(buffer)

    def pct_str(self, figure, total):
        if total == 0:
            return "NaN"

        percentage = 100.0 * float(figure) / float(total)
        return str(round(percentage, 1)) + "%"

    def generate_list(self, users, anon_id, pub_project_pairs, priv_project_pairs):
        """
        See the module documentation.

        :param pub_project_pairs: (project id, trac environment key) pairs of the projects which anon user can see
        :param priv_project_pairs: (project id, trac environment key) pairs of other than public projects
        :param users: A list of :py:class:`User` objects
        :returns: A list of table ProjectUserVisibility objects for (project_id, user_id)
        """
        # TODO: Horribly slow way to implement, optimize!
        # For each user, search ldap groups and organization where the
        # user belongs. For each project, search every group having the
        # required permission, and obtain the single user, ldap group or
        # organization members of those groups. Add a cache-awareness
        # into CQDEPermissionStore.check_permission() method (for example,
        # allow passing groups, organization_members and ldap_groups).
        if self.verbose:
            print "using cnc/pfnc rules to generate the visibility list"
        iter = 0
        target = len(users) * len(priv_project_pairs)
        percent = int(target / 100)
        if percent == 0:
            percent = 1
        big_list = []
        if pub_project_pairs:
            for proj_id, trac_environment_key in pub_project_pairs:
                big_list.append(ProjectUserVisibility(proj_id, anon_id))

        trues = len(pub_project_pairs or [])
        falses = 0
        last = time()
        for proj_id, trac_environment_key in priv_project_pairs:
            for user in users:
                iter += 1
                if self.user_can_view_project(trac_environment_key, user.username):
                    trues += 1
                    big_list.append(ProjectUserVisibility(proj_id, user.user_id))
                else:
                    falses += 1
                if self.verbose and iter % percent == 0:
                    print "progress:", ((100 * iter) / target + 1) , "%, delta time for", percent, "iterations: ", (time() - last), " seconds"
                    last = time()

        return big_list, trues, falses
Пример #5
0
class ProjectUserVisibilityGenerator():
    def __init__(self, verbose=False):
        """
        If verbose = None, be absolutely quiet
        """
        self.verbose = verbose
        self.policy = CQDEPermissionPolicy(MockEnvironment())
        self.papi = Projects()
        self.batch_size = conf.visibility_db_batch_size
        self.required_permission = 'PROJECT_VIEW'

    def get_anonymous_user_id(self):
        anon = get_userstore().getUser('anonymous')
        if anon:
            anon_id = safe_int(anon.id)
        else:
            anon_id = None

        return anon_id

    def get_public_project_pairs(self, anon_id):
        """
        :param anon_id: Anonymous user_id
        :return: List of tuples (project_id, trac_environment_key)
        """
        # TODO: it's not clearly defined when project is public
        perms = ['PROJECT_VIEW', 'TRAC_ADMIN']
        perm_ids = [get_permission_id(perm) for perm in perms]

        query = """SELECT DISTINCT p.project_id, p.trac_environment_key
                          FROM projects p
                    INNER JOIN `group` g ON g.trac_environment_key = p.trac_environment_key
                    INNER JOIN user_group ON user_group.group_key = g.group_id
                    INNER JOIN group_permission ON group_permission.group_key = g.group_id
                         WHERE user_group.user_key = {0}
                           AND group_permission.permission_key IN ({1})
                          """.format(
            anon_id, ', '.join([str(int(id)) for id in perm_ids]))

        id_pairs = []
        with admin_query() as cursor:
            try:
                cursor.execute(query)
                for row in cursor:
                    id_pair = (int(row[0]), int(row[1]))
                    id_pairs.append(id_pair)
            except Exception as e:
                if self.verbose is not None:
                    print "Exception. In method get_public_project_pairs, the following query failed."
                    print query
                    print e
                conf.log.exception(
                    "Exception. ProjectUserVisibilityGenerator.get_public_project_pairs "
                    "failed with query '''%s'''." % query)

        return id_pairs

    def get_private_project_pairs(self, public_id_pairs=None):
        query = "SELECT project_id, trac_environment_key FROM projects "

        if public_id_pairs:
            public_project_ids = [pair[0] for pair in public_id_pairs]
            # TODO: What if there are really many public projects?
            pub_projs = ','.join(str(id) for id in public_project_ids)
            query += "WHERE projects.project_id NOT IN (%s)" % pub_projs

        id_pairs = []
        with admin_query() as cursor:
            try:
                cursor.execute(query)
                for row in cursor:
                    id_pair = (int(row[0]), int(row[1]))
                    id_pairs.append(id_pair)
            except Exception as e:
                if self.verbose is not None:
                    print "Exception. In method get_private_project_pairs, the following query failed."
                    print query
                    print e
                conf.log.exception(
                    "Exception. ProjectUserVisibilityGenerator.get_private_project_pairs failed "
                    "with query '''%s'''." % query)

        return id_pairs

    def get_all_users(self, except_anon=True):
        """
        Returns :py:class:`User` objects for every user except 'authenticated' and, if
        anon_id is given, 'anonymous'.

        No row for 'authenticated' users are generated currently, this is why the 'authenticated'
        user is left out.

        :param bool except_anon: when True, don't include anon user
        :returns: A list of :py:class:`User` objects
        """
        if except_anon:
            query = """SELECT user_id, username FROM `user`
                        WHERE username NOT IN ('anonymous', 'authenticated') """
        else:
            query = """SELECT user_id, username FROM `user`
                        WHERE username NOT IN ('authenticated') """

        users = []
        with admin_query() as cursor:
            try:
                cursor.execute(query)
                for row in cursor:
                    user = User.from_sql_row(row)
                    users.append(user)
            except Exception as e:
                if self.verbose is not None:
                    print "Exception. In method get_all_users, the following query failed."
                    print query
                    print e
                conf.log.exception(
                    "Exception. ProjectUserVisibilityGenerator.get_all_users failed "
                    "with query '''%s'''." % query)

        return users

    def user_can_view_project(self, trac_environment_key, username):
        return self.policy.check_permission(trac_environment_key,
                                            self.required_permission, username)

    def clear_project_visibilities(self, project_id):
        query = "DELETE FROM project_user_visibility WHERE project_id = %s"

        with admin_transaction() as cursor:
            try:
                cursor.execute(query, project_id)
            except Exception as e:
                if self.verbose is not None:
                    print "Exception. In method clear_visibilities, the following query failed."
                    print query
                    print e
                conf.log.exception(
                    "Exception. ProjectUserVisibilityGenerator.clear_visibilities "
                    "failed with query '''%s'''." % query)

    def batch_insert(self, visibilities):
        query = "INSERT INTO project_user_visibility (project_id, user_id) VALUES "

        query += ",".join([
            "(%d,%d)" %
            (safe_int(visibility.project_id), safe_int(visibility.user_id))
            for visibility in visibilities
        ])

        with admin_transaction() as cursor:
            try:
                cursor.execute(query)
            except Exception as e:
                if self.verbose is not None:
                    print "Exception. In method batch_insert, the following query failed."
                    print query
                    print e
                conf.log.exception(
                    "Exception. ProjectUserVisibilityGenerator.batch_insert '''%s'''."
                    % query)

    def flush_buffer(self):
        if len(self.buffer) > 0:
            self.batch_insert(self.buffer)
            self.buffer = []

    def insert_visibilities(self, buffer):
        project_visibilities = []
        project_id = buffer[0].project_id
        for visibility in buffer:
            if project_id == visibility.project_id:
                project_visibilities.append(visibility)
            else:
                self.clear_project_visibilities(
                    project_visibilities[0].project_id)
                self.buffered_insert(project_visibilities)
                project_visibilities = [visibility]
                project_id = visibility.project_id

        if project_visibilities:
            self.clear_project_visibilities(project_visibilities[0].project_id)
            self.buffered_insert(project_visibilities)

    def buffered_insert(self, buffer):
        while len(buffer) >= self.batch_size:
            self.batch_insert(buffer[:self.batch_size])
            buffer = buffer[self.batch_size:]

        if len(buffer) > 0:
            self.batch_insert(buffer)

    def pct_str(self, figure, total):
        if total == 0:
            return "NaN"

        percentage = 100.0 * float(figure) / float(total)
        return str(round(percentage, 1)) + "%"

    def generate_list(self, users, anon_id, pub_project_pairs,
                      priv_project_pairs):
        """
        See the module documentation.

        :param pub_project_pairs: (project id, trac environment key) pairs of the projects which anon user can see
        :param priv_project_pairs: (project id, trac environment key) pairs of other than public projects
        :param users: A list of :py:class:`User` objects
        :returns: A list of table ProjectUserVisibility objects for (project_id, user_id)
        """
        # TODO: Horribly slow way to implement, optimize!
        # For each user, search ldap groups and organization where the
        # user belongs. For each project, search every group having the
        # required permission, and obtain the single user, ldap group or
        # organization members of those groups. Add a cache-awareness
        # into CQDEPermissionStore.check_permission() method (for example,
        # allow passing groups, organization_members and ldap_groups).
        if self.verbose:
            print "using cnc/pfnc rules to generate the visibility list"
        iter = 0
        target = len(users) * len(priv_project_pairs)
        percent = int(target / 100)
        if percent == 0:
            percent = 1
        big_list = []
        if pub_project_pairs:
            for proj_id, trac_environment_key in pub_project_pairs:
                big_list.append(ProjectUserVisibility(proj_id, anon_id))

        trues = len(pub_project_pairs or [])
        falses = 0
        last = time()
        for proj_id, trac_environment_key in priv_project_pairs:
            for user in users:
                iter += 1
                if self.user_can_view_project(trac_environment_key,
                                              user.username):
                    trues += 1
                    big_list.append(
                        ProjectUserVisibility(proj_id, user.user_id))
                else:
                    falses += 1
                if self.verbose and iter % percent == 0:
                    print "progress:", (
                        (100 * iter) / target +
                        1), "%, delta time for", percent, "iterations: ", (
                            time() - last), " seconds"
                    last = time()

        return big_list, trues, falses
Пример #6
0
    def check_permission(self, action, username, resource, perm):
        """
        Checks permissions - Actual checking is done on CQDEPermissionPolicy class
        """
        # FIXME: Dirty hack to screw ILegacyAttachmentPolicy.
        perm_maps = {
            "ATTACHMENT_CREATE": {
                "ticket": "TICKET_APPEND",
                "wiki": "WIKI_MODIFY",
                "milestone": "MILESTONE_MODIFY",
                "discussion": "DISCUSSION_ATTACH",
            },
            "ATTACHMENT_VIEW": {
                "ticket": "TICKET_VIEW",
                "wiki": "WIKI_VIEW",
                "milestone": "MILESTONE_VIEW",
                "discussion": "DISCUSSION_ATTACH",
            },
            "ATTACHMENT_DELETE": {
                "ticket": "TICKET_ADMIN",
                "wiki": "WIKI_DELETE",
                "milestone": "MILESTONE_DELETE",
                "discussion": "DISCUSSION_ATTACH",
            },
        }
        perm_map = perm_maps.get(action)
        if perm_map and resource and resource.realm == "attachment":
            action = perm_map.get(resource.parent.realm)

        policy = CQDEPermissionPolicy(self.env)

        # Project context check
        if resource and resource.realm == "project":
            # NOTE: Load project to get environment key required by check_permission
            # NOTE: Internal TracEnvironment cannot be used because env can be home, whereas project id is not
            project = Project.get(id=resource.id)
            if project and policy.check_permission(project.trac_environment_key, action, username):
                return True
            return False

        # Ticket authors should be able to edit their own tickets
        # (excluding 'anonymous')
        if (
            username != "anonymous"
            and resource
            and resource.id
            and resource.realm == "ticket"
            and action in ("TICKET_CHGPROP", "TICKET_EDIT_DESCRIPTION")
        ):
            ticket = Ticket(self.env, int(resource.id))
            if ticket.exists and username == ticket["reporter"]:
                return True

        # Load lightweight trac environment to get environment id, required by internal check_permission
        env_name = conf.resolveProjectName(self.env)
        environment = TracEnvironment.read(env_name)

        # Check permission using global permission policy and storage
        if not policy.check_permission(environment.environment_id, action, username):
            return False

        # Additional, resources based checks

        # User author check
        if action in ("USER_ADMIN", "USER_AUTHOR", "USER_VIEW", "USER_MODIFY", "USER_DELETE") and resource:
            # Check if USER_ADMIN permission in home project
            home_perm = PermissionCache(conf.home_env, username)
            if "USER_ADMIN" in home_perm:
                return True

            userstore = conf.getUserStore()
            resource_user = userstore.getUserWhereId(resource.id)
            user = userstore.getUser(username)

            # Allow manage own and authored account
            if action in ("USER_ADMIN", "USER_AUTHOR"):
                return resource_user.author_id == user.id or resource_user.id == user.id

            # Allow to manage itself
            return resource_user.id == user.id

        return True