Esempio n. 1
0
 def is_local(self, user):
     """
     Check if given user is local or not
     :param User user: User to check
     :returns: True if user is a local user, otherwise False
     """
     from multiproject.core.authentication import CQDEAuthenticationStore
     auth_store = CQDEAuthenticationStore.instance()
     return auth_store.is_local(user.authentication_key)
Esempio n. 2
0
    def __init__(self):

        self.auth_store = CQDEAuthenticationStore.instance()
        self.local_authentication_key = self.auth_store.get_authentication_id(self.LOCAL)
        if not self.local_authentication_key:
            self.auth_store.create_authentication(self.LOCAL)
            self.local_authentication_key = self.auth_store.get_authentication_id(self.LOCAL)
        if not self.local_authentication_key:
            # This should not happen
            raise Exception('LocalAuthentication: Could not get authentication id for LocalDB')
Esempio n. 3
0
    def _init_known_authentications(self):
        """ Lazy load authentications and store them
            locally to reduce sql
        """
        auth_store = CQDEAuthenticationStore.instance()
        auths = auth_store.get_authentications()

        for auth in auths:
            self.authentications[auth.id] = auth.name
        self.authentications[None] = '<No authentication>'
Esempio n. 4
0
    def _init_known_authentications(self):
        """ Lazy load authentications and store them
            locally to reduce sql
        """
        auth_store = CQDEAuthenticationStore.instance()
        auths = auth_store.get_authentications()

        for auth in auths:
            self.authentications[auth.id] = auth.name
        self.authentications[None] = '<No authentication>'
Esempio n. 5
0
    def __init__(self):

        self.org_store = CQDEOrganizationStore.instance()
        self.auth_store = CQDEAuthenticationStore.instance()
        self.ldap_authentication_key = self.auth_store.get_authentication_id(self.LDAP)
        if not self.ldap_authentication_key:
            self.auth_store.create_authentication(self.LDAP)
            self.ldap_authentication_key = self.auth_store.get_authentication_id(self.LDAP)
        if not self.ldap_authentication_key:
            # This should not happen
            raise Exception('LdapAuthentication: Could not get authentication id for LDAP')
Esempio n. 6
0
    def __init__(self):

        self.org_store = CQDEOrganizationStore.instance()
        self.auth_store = CQDEAuthenticationStore.instance()
        self.ldap_authentication_key = self.auth_store.get_authentication_id(
            self.LDAP)
        if not self.ldap_authentication_key:
            self.auth_store.create_authentication(self.LDAP)
            self.ldap_authentication_key = self.auth_store.get_authentication_id(
                self.LDAP)
        if not self.ldap_authentication_key:
            # This should not happen
            raise Exception(
                'LdapAuthentication: Could not get authentication id for LDAP')
    def upgrade(self):
        if self.applied():
            print "Migration already applied".rjust(12)
            return True

        auth_store = CQDEAuthenticationStore.instance()
        id = auth_store.get_authentication_id(auth_store.LOCAL)
        if not id:
            print "Error: local authentication id not found: name is %s" % auth_store.LOCAL
            return False

        queries = ["""
        UPDATE `user`
           SET SHA1_PW = 'invalidNonLocalUserPwHash'
         WHERE authentication_key <> %s
        """ % safe_int(id)]

        return self.manager.db_upgrade(queries)
    def applied(self):
        """
        Check if column exists or not
        :returns: True if exists, otherwise False
        """
        if self._pretend_not_applied:
            return False

        count = 0
        auth_store = CQDEAuthenticationStore.instance()
        id = auth_store.get_authentication_id(auth_store.LOCAL)
        with admin_query() as cursor:
            cursor.execute("""
            SELECT COUNT(*) AS count
              FROM `user`
             WHERE SHA1_PW <> 'invalidNonLocalUserPwHash'
               AND authentication_key <> %s """, (id,))
            count = int(cursor.fetchone()[0])

        return count == 0
    def upgrade(self):
        if self.applied():
            print "Migration already applied".rjust(12)
            return True

        auth_store = CQDEAuthenticationStore.instance()
        id = auth_store.get_authentication_id(auth_store.LOCAL)
        if not id:
            print "Error: local authentication id not found: name is %s" % auth_store.LOCAL
            return False

        queries = [
            """
        UPDATE `user`
           SET SHA1_PW = 'invalidNonLocalUserPwHash'
         WHERE authentication_key <> %s
        """ % safe_int(id)
        ]

        return self.manager.db_upgrade(queries)
Esempio n. 10
0
    def invalidate_user_password(self, user):
        """
        Changes the user password to 'invalidNonLocalUserPwHash' for non-local user
        Clears caching as in updatePassword

        .. Note ::

            Used only for non-local users!
            SHA1_PW must not contain space characters, since it is used in memcache key

        """
        self.__cache.clear_user_by_user(user)

        if not user.id:
            return False
        from multiproject.core.authentication import CQDEAuthenticationStore
        auth_store = CQDEAuthenticationStore.instance()
        query = """UPDATE `user`
               INNER JOIN authentication ON `user`.authentication_key = authentication.id
                      SET `user`.SHA1_PW = 'invalidNonLocalUserPwHash'
                    WHERE user_id = %s
                      AND authentication.method <> %s"""

        result = True
        # Do invalidation
        with admin_transaction() as cursor:
            try:
                cursor.execute("SELECT SHA1_PW FROM user WHERE user_id = %s", user.id)
                sha = cursor.fetchone()
                self.__authcache.clearAuthentication(user.username, str(sha[0]).encode('utf-8'))
                if sha[0] != 'invalidNonLocalUserPwHash':
                    # If not already invalid password hash
                    cursor.execute(query, (user.id, auth_store.LOCAL))
                    if not cursor.rowcount:
                        conf.log.error("Nothing affected when invalidating password for user %s"
                                       % user.username)
                        result = False
            except Exception:
                conf.log.exception("Failed to invalidate password for user %s." % user.username)
                return False
        return result
    def applied(self):
        """
        Check if column exists or not
        :returns: True if exists, otherwise False
        """
        if self._pretend_not_applied:
            return False

        count = 0
        auth_store = CQDEAuthenticationStore.instance()
        id = auth_store.get_authentication_id(auth_store.LOCAL)
        with admin_query() as cursor:
            cursor.execute(
                """
            SELECT COUNT(*) AS count
              FROM `user`
             WHERE SHA1_PW <> 'invalidNonLocalUserPwHash'
               AND authentication_key <> %s """, (id, ))
            count = int(cursor.fetchone()[0])

        return count == 0
Esempio n. 12
0
    def render_admin_panel(self, req, cat, page, path_info):
        """ Renders admin panel and handles new user creation request
        """
        req.perm.require('USER_CREATE')

        now = datetime.utcnow()
        expires = now + timedelta(days=90)
        data = {
            'dateformats': DATEFORMATS,
            'now': now,
            'expires': expires,
        }
        # Helper class
        add_script(req, 'multiproject/js/multiproject.js')
        add_script(req, 'multiproject/js/admin_user_create.js')

        # Get and set option goto address
        if 'goto' in req.args:
            req.session['goto'] = conf.safe_address(req.args.get('goto', ''))
            req.session.save()

        # Create new user to local database
        if req.method.upper() == 'GET':
            return 'admin_user_create.html', data

        elif req.method.upper() == 'POST':
            userstore = get_userstore()
            user = self._get_user(req)
            author = userstore.getUser(req.authname)

            # Update data for pre-filled form
            data['username'] = user.username
            data['first'] = user.givenName
            data['last'] = user.lastName
            data['mail'] = user.mail
            data['mobile'] = user.mobile

            # Validate and set author
            if not req.perm.has_permission('USER_AUTHOR') or not author:
                chrome.add_warning(
                    req,
                    _("User needs to have author with USER_AUTHOR permission"))
                return 'admin_user_create.html', data
            user.author_id = author.id
            user.expires = expires

            org_store = CQDEOrganizationStore.instance()
            auth_store = CQDEAuthenticationStore.instance()
            user.authentication_key = auth_store.get_authentication_id(
                LocalAuthentication.LOCAL)
            user.organization_keys = org_store.get_organization_keys(
                user, LocalAuthentication.LOCAL) or None

            # Validate user object
            error_msg = self.validate_user(req, user)
            if error_msg:
                chrome.add_warning(req, error_msg)
                return 'admin_user_create.html', data

            # Try to store user
            if userstore.storeUser(user):
                userlink = tag.a(user.username,
                                 href=req.href('admin/users/manage',
                                               username=user.username))
                chrome.add_notice(req,
                                  tag(_('Created new local user: '******'Created new local user "%s" by "%s"' %
                              (user.username, req.authname))

                # Try to send email notification also
                try:
                    self._send_notification(user, req.server_name)
                except TracError:
                    # Notification sending failed
                    self.log.exception("Notification sending failed")
                    chrome.add_warning(req,
                                       _('Failed to send email notification'))

                # Handle optional goto argument
                if 'goto' in req.session:
                    goto = req.session['goto']
                    del req.session['goto']

                    # NOTE: Show redirect address as a system message instead of direct redirection
                    # This is because after moving to another project, the system messages are not shown due the separate
                    # sessions per project
                    chrome.add_notice(
                        req,
                        Markup('Go back to: <a href="%s">%s</a>' %
                               (goto, goto)))

                # Redirect to the page so that we're not showing the created user form with prefilled
                return req.redirect(req.href('admin/users/create_local'))

            return 'admin_user_create.html', data
Esempio n. 13
0
    def render_admin_panel(self, req, cat, page, path_info):
        """ Renders admin panel and handles new user creation request
        """
        req.perm.require('USER_CREATE')

        now = datetime.utcnow()
        expires = now + timedelta(days=90)
        data = {
            'dateformats':DATEFORMATS,
            'now':now,
            'expires':expires,
        }
        # Helper class
        add_script(req, 'multiproject/js/multiproject.js')
        add_script(req, 'multiproject/js/admin_user_create.js')

        # Get and set option goto address
        if 'goto' in req.args:
            req.session['goto'] = conf.safe_address(req.args.get('goto', ''))
            req.session.save()

        # Create new user to local database
        if req.method.upper() == 'GET':
            return 'admin_user_create.html', data

        elif req.method.upper() == 'POST':
            userstore = get_userstore()
            user = self._get_user(req)
            author = userstore.getUser(req.authname)

            # Update data for pre-filled form
            data['username'] = user.username
            data['first'] = user.givenName
            data['last'] = user.lastName
            data['mail'] = user.mail
            data['mobile'] = user.mobile

            # Validate and set author
            if not req.perm.has_permission('USER_AUTHOR') or not author:
                chrome.add_warning(req, _("User needs to have author with USER_AUTHOR permission"))
                return 'admin_user_create.html', data
            user.author_id = author.id
            user.expires = expires

            org_store = CQDEOrganizationStore.instance()
            auth_store = CQDEAuthenticationStore.instance()
            user.authentication_key = auth_store.get_authentication_id(LocalAuthentication.LOCAL)
            user.organization_keys = org_store.get_organization_keys(user, LocalAuthentication.LOCAL) or None

            # Validate user object
            error_msg = self.validate_user(req, user)
            if error_msg:
                chrome.add_warning(req, error_msg)
                return 'admin_user_create.html', data

            # Try to store user
            if userstore.storeUser(user):
                userlink = tag.a(user.username, href=req.href('admin/users/manage', username=user.username))
                chrome.add_notice(req, tag(_('Created new local user: '******'Created new local user "%s" by "%s"' % (user.username, req.authname))

                # Try to send email notification also
                try:
                    self._send_notification(user)
                except TracError:
                    # Notification sending failed
                    self.log.exception("Notification sending failed")
                    chrome.add_warning(req, _('Failed to send email notification'))

                # Handle optional goto argument
                if 'goto' in req.session:
                    goto = req.session['goto']
                    del req.session['goto']

                    # NOTE: Show redirect address as a system message instead of direct redirection
                    # This is because after moving to another project, the system messages are not shown due the separate
                    # sessions per project
                    chrome.add_notice(req, Markup('Go back to: <a href="%s">%s</a>' % (goto, goto)))

                # Redirect to the page so that we're not showing the created user form with prefilled
                return req.redirect(req.href('admin/users/create_local'))

            return 'admin_user_create.html', data
Esempio n. 14
0
 def __init__(self):
     self.auth_store = CQDEAuthenticationStore.instance()
     self.primary_auth_method = conf.authentication_order[0].lower()
Esempio n. 15
0
    def check_permission(self, trac_environment_id, permission, check_username):
        """
        Helper class for the GlobalPermissionPolicy.check_permission,
        which checks also the resource, unlike this.
        Checks permission for the user in the trac environment.
        """
        # Disable completely features by appending actions into this list
        restricted_features = ['REPORT_CREATE', 'REPORT_SQL_VIEW']
        if permission in restricted_features:
            return False

        # Get user in question
        user = get_userstore().getUser(check_username)
        store = CQDEUserGroupStore(trac_environment_id)
        superusers = CQDESuperUserStore.instance()

        # If there is no user then there is no need for permission check
        if not user:
            return False

        # Check if user is a superuser
        if superusers.is_superuser(user.username):
            return True

        # Get all groups and users
        user_groups = store.get_all_user_groups()
        group_perms = store.get_all_group_permissions()
        organization_groups = store.get_all_organization_groups()

        # List groups that have the permission
        groups = []
        for group, perm in group_perms:
            # NOTE: Also extend the meta permissions into list
            perms = [perm] + self.meta_perms.get(perm, [])
            if permission in perms:
                groups.append(group)

        users = get_special_users(user.username)
        users.append(user.username)

        # See if user is in one of the groups
        for username, group in user_groups:
            if username in users:
                if group in groups:
                    return True

        # See if user's organization is in one of the groups
        org_store = CQDEOrganizationStore.instance()
        for org, group in organization_groups:
            org_id = org_store.get_organization_id(org)
            if org_id in user.organization_keys:
                if group in groups:
                    return True

        if conf.ldap_groups_enabled:
            # TODO: do not use CQDEAuthenticationStore to check this, the information should
            #       be available from User object (add if not!)
            # See if any ldap groups are allowed in environment
            from multiproject.core.authentication import CQDEAuthenticationStore

            auth_store = CQDEAuthenticationStore.instance()
            is_ldap_account = auth_store.is_ldap(user.authentication_key)
            trac_environment_ldapgroups = store.get_all_trac_environment_ldap_groups()
            if is_ldap_account and trac_environment_ldapgroups:
                # See if user belongs to any of the allowed ldap groups
                ldapuser_store = conf.getAuthenticationStore()
                user_ldapgroups = ldapuser_store.getGroups(user.username)
                for ldapgroup, group in trac_environment_ldapgroups:
                    if ldapgroup in user_ldapgroups:
                        if group in groups:
                            return True

        return False
Esempio n. 16
0
 def __init__(self):
     self.auth_store = CQDEAuthenticationStore.instance()
     self.primary_auth_method = conf.authentication_order[0].lower()
Esempio n. 17
0
    def get_participated_projects(self, user, by_organization=False, by_ldap=False,
                                  public_only=False):
        """
        Get those projects that user has participated. Optionally can list by organization,
        or by public status.

        :param User user: User object
        :param boolean by_organization: List projects by organization
        :param boolean public_only: Get public projects as well
        :returns: List of Project objects
        """

        # Anonymous does not have organization
        if not user.organization_keys:
            by_organization = False

        and_anon_condition= ''
        union_all_organization_condition = ''
        union_all_ldap_condition = ''
        perm_ids = ', '.join([str(safe_int(get_permission_id(action)))
                              for action in ('TEAM_VIEW',)])
        if public_only:
            # fetch anonymous user id
            # FIXME: Would be nice if we didn't have to do this all the time
            anon = get_userstore().getUser('anonymous')
            if not anon:
                conf.log.warning("Error in get_participated_projects: No anonymous user obtained!")
                raise TracError("Error while fetching user's projects.")
            and_anon_condition = """
                AND EXISTS
                (SELECT group.trac_environment_key
                FROM `group`
                INNER JOIN user_group ON user_group.group_key = group.group_id
                INNER JOIN group_permission ON group.group_id = group_permission.group_key
                WHERE user_group.user_key = {anon_id}
                  AND group.trac_environment_key = projects.trac_environment_key
                  AND group_permission.permission_key IN ({perm_ids})
                )
            """.format(anon_id = safe_int(anon.id), perm_ids = perm_ids)

        if by_organization:
            # See if the user has access into projects through organizations
            union_all_organization_condition += """
                UNION ALL

                SELECT group.trac_environment_key
                FROM `trac_admin`.`group`
                INNER JOIN organization_group ON organization_group.group_key = group.group_id
                WHERE organization_group.organization_key IN({organization_ids})
            """.format(organization_ids =
                ', '.join([str(safe_int(org_id)) for org_id in user.organization_keys]))

        if by_ldap and conf.ldap_groups_enabled:
            # See if any ldap groups are allowed in environment
            auth_store = CQDEAuthenticationStore.instance()
            is_ldap_account = auth_store.is_ldap(user.authentication_key)
            if is_ldap_account:
                # See if user belongs to any of the allowed ldap groups
                ldapuser_store = conf.getAuthenticationStore()
                user_ldapgroups = ldapuser_store.getGroups(user.username)

                if user_ldapgroups:
                    union_all_ldap_condition = """
                    UNION ALL

                    SELECT group.trac_environment_key
                    FROM `trac_admin`.`group`
                    INNER JOIN ldapgroup_group ON ldapgroup_group.group_key = group.group_id
                    INNER JOIN ldapgroup ON ldapgroup.ldapgroup_id = ldapgroup_group.ldapgroup_key
                    WHERE ldapgroup.ldapgroup_name IN ({ldapgroup_names})
                    """.format(ldapgroup_names =
                        ', '.join(["'{0}'".format(safe_string(group)) for group in user_ldapgroups]))

        query = """
            SELECT projects.* FROM projects
            WHERE projects.trac_environment_key IN (
                SELECT group.trac_environment_key
                FROM `trac_admin`.`group`
                INNER JOIN user_group ON user_group.group_key = group.group_id
                WHERE user_group.user_key = {user_id}
                {union_all_organization}
                {union_all_ldap}
            )
            {and_anon}
            ORDER BY projects.project_name ASC
        """.format(user_id = safe_int(user.id),
            union_all_organization = union_all_organization_condition,
            union_all_ldap = union_all_ldap_condition,
            and_anon = and_anon_condition)

        conf.log.debug("queried participated projects with %s" % query)
        projects = self.queryProjectObjects(query)
        return projects
Esempio n. 18
0
    def get_organization_keys(self, user, auth_method=None):
        """
        Returns list of organization keys matching with the user (and optionally authentication method)

        Each authentication method has corresponding organization,
        which are applied to the user by default, when user is created

        :param User user: User to get organization keys for
        :param str auth_method:
            Name of the authentication method / backend.
            Valid values: LocalDB,LDAP,

        Example::

            from multiproject.core.auth.local_auth import LocalAuthentication
            from multiproject.core.users import get_userstore

            userstore = get_userstore()
            user = userstore.getUser('name')

            get_organization_keys(user, LocalAuthentication.LOCAL)

        """
        from multiproject.common.projects import HomeProject
        from multiproject.common.users import OrganizationManager

        organization_ids = []

        # Load home env to load component
        self.env = HomeProject().get_env()
        orgman = self.env[OrganizationManager]

        if not orgman.use_organizations:
            return []

        # If authentication method/backend is not defined, load info from user
        if not auth_method:
            # TODO: do not use CQDEAuthenticationStore to check this, the information should
            #       be available from User object (add if not!)
            from multiproject.core.authentication import CQDEAuthenticationStore

            auth_store = CQDEAuthenticationStore.instance()
            auth_method = auth_store.get_authentication_method(user.authentication_key)

        if auth_method:
            try:
                # Iterate all organization names that matches with authentication backend name
                be_orgs = [org for org in orgman.get_organizations_by_backend(auth_method) if org['type'] == 'auth']
                if not be_orgs:
                    conf.log.info('Failed to find backend based organization info: %s' % auth_method)

                for org in be_orgs:
                    id = int(self.get_organization_id(org['name']))
                    if id and id not in organization_ids:
                        organization_ids.append(id)
            except:
                conf.log.exception("Exception on auth_method")

        # Check if mail based organizations are defined
        if user.mail:
            try:
                # Iterate all organization names that matches with email domain: @domain.com
                mailhost = "@" + user.mail.split("@")[1]
                mail_orgs = [org for org in orgman.get_organizations_by_backend(mailhost) if org['type'] == 'email']
                if not mail_orgs:
                    conf.log.info('Failed to find email based organization info: %s' % mailhost)

                for org in mail_orgs:
                    id = int(self.get_organization_id(org['name']))
                    if id and id not in organization_ids:
                        organization_ids.append(id)

            except:
                conf.log.exception("Failed to read email based organization info")

        conf.log.debug('Found organization ids %s for user %s' % (organization_ids, user))

        return organization_ids
Esempio n. 19
0
    def storeUser(self, user):
        """
        Function for writing user into persistent storage

        .. IMPORTANT::

            Function also saves the password hash into database.
            If non-local user, pw hash will be 'invalidNonLocalUserPwHash'.

        :returns: True on success, False on failure
        """
        self.__cache.clearUser(user.username)

        # Set defaults for optional fields
        user.mobile = user.mobile or ''
        user.password = user.password or ''
        user.givenName = user.givenName or ''
        user.lastName = user.lastName or ''
        user.icon = safe_int(user.icon) or None
        user.created = user.created or datetime.utcnow()
        user.expires = user.expires or None  # By default, no expire date
        user.author_id = user.author_id or None  # By default, no author/owner information

        # Check the required fields: username and password
        if not user.username:
            conf.log.error('User object is missing username - giving up user store')
            return False

        auth_store = CQDEAuthenticationStore.instance()
        local_authentication_key = auth_store.get_authentication_id(auth_store.LOCAL)

        if not user.authentication_key:
            user.authentication_key = local_authentication_key

        # Set default status id based on name defined configuration
        default_status_name = conf.default_user_status.lower()
        status_key = self.USER_STATUS_KEYS.get(default_status_name)
        if not status_key:
            self.log.error('Failed to find and set user default status')
            return False

        sha1_pw = 'SHA1(%s)'
        if user.authentication_key != local_authentication_key:
            user.password = '******'
            sha1_pw = '%s'

        # SQL query for creating a new user
        query = """
        INSERT INTO user (
            user_id, username, mail, mobile, givenname, lastname, created,
            icon_id, SHA1_PW, authentication_key, user_status_key,
            expires, author_id
        )
        VALUES (NULL, %s, %s, %s, %s, %s, %s, %s, {sha1_pw}, %s, %s, %s, %s)
        """.format(sha1_pw=sha1_pw)

        # SQL params
        params = (
            user.username, user.mail, user.mobile,
            user.givenName.encode('utf-8'), user.lastName.encode('utf-8'), user.created, user.icon,
            user.password.encode('utf-8'), str(user.authentication_key), str(status_key),
            user.expires, user.author_id
        )

        with admin_transaction() as cursor:
            try:
                cursor.execute(query, params)
                user.id = cursor.lastrowid
            except:
                conf.log.exception("Failed to create user")
                raise

        self.storeUserOrganizations(user)
        self.updateUserPreferences(user)

        return True