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

        org_store = CQDEOrganizationStore.instance()
        user = User()
        organization_ids = org_store.get_organization_keys(user, LOCAL)

        if not organization_ids or len(organization_ids) > 1:
            print "Local organization was not found!"
            return False

        local_organization_id = organization_ids[0]

        queries = [
            """
                INSERT INTO user_organization (user_key, organization_key)
                SELECT user_id, {organization_id}
                FROM user WHERE user_id NOT IN (SELECT user_key FROM user_organization)
                AND authentication_key IN (SELECT id FROM authentication
                                           WHERE method = '{method}')
           """.format(organization_id=local_organization_id, method=LOCAL)
        ]
        return self.manager.db_upgrade(queries)
Beispiel #2
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 __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

        org_store = CQDEOrganizationStore.instance()
        user = User()
        organization_ids = org_store.get_organization_keys(user, LOCAL)

        if not organization_ids or len(organization_ids) > 1:
            print "Local organization was not found!"
            return False

        local_organization_id = organization_ids[0]

        queries = ["""
                INSERT INTO user_organization (user_key, organization_key)
                SELECT user_id, {organization_id}
                FROM user WHERE user_id NOT IN (SELECT user_key FROM user_organization)
                AND authentication_key IN (SELECT id FROM authentication
                                           WHERE method = '{method}')
           """.format(organization_id=local_organization_id, method=LOCAL)]
        return self.manager.db_upgrade(queries)
Beispiel #5
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
Beispiel #6
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
Beispiel #7
0
    def edit_user(self, req):
        """
        Handle user edit: view & save
        """
        changes = {}

        username = req.args.get('username')
        if not username:
            add_warning(req, _('Invalid username'))
            return self.list_users(req)

        # Load user being edited
        userstore = get_userstore()
        user = userstore.getUser(username)
        if not user:
            add_warning(req, _('Invalid username (or connection error)'))
            return self.list_users(req)

        # Load user who's doing the edit
        changed_by = userstore.getUser(req.authname)
        papi = Projects()

        # Check permissions and redirect to user listing (handy after editing the user)
        req.perm.require('USER_AUTHOR', Resource('user', id=user.id))

        data = req.args
        data['user'] = user
        data['author'] = userstore.getUserWhereId(user.author_id) if user.author_id else None
        data['base_path'] = req.base_path
        data['dateformats'] = DATEFORMATS
        data['is_local'] = userstore.is_local(user)
        data['now'] = datetime.utcnow()
        data['expired'] = user.expires and ((user.expires - datetime.utcnow()).days < 0)
        data['states'] = userstore.USER_STATUS_LABELS
        data['projects'] = papi.get_authored_projects(user)

        # Add javascript libraries for datepicker and autocomplete
        add_script(req, 'multiproject/js/jquery-ui.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        add_script(req, 'multiproject/js/multiproject.js')
        add_script(req, 'multiproject/js/admin_user_edit.js')

        # If get request show edit
        if req.method.upper() == 'GET':
            return 'admin_user_edit.html', data

        # Close pressed: get back to user listing
        if req.args.get('close'):
            return req.redirect(req.href('admin/users/manage'))

        # Handle save
        if 'limitexceeded' in req.args:
            add_warning(req, _('Picture you tried to upload was too big. Try a smaller one'))

        # Update author if changed
        author_id = req.args.get('author_id', None)

        # If id field is empty but name is not: manual input
        if not author_id and req.args.get('author_text'):
            add_warning(req, _('Author cannot be found'))
            return 'admin_user_edit.html', data

        # Check set reset the author
        if author_id:
            author = userstore.getUserWhereId(int(author_id))
            if not author:
                add_warning(req, _('Author cannot be found'))
                return 'admin_user_edit.html', data

            # Check if author is valid: has permission to author?
            perm = PermissionCache(self.env, author.username)
            if 'USER_AUTHOR' not in perm:
                add_warning(req, _('User %s cannot work as an author (does not have USER_AUTHOR permissions)' % author))
                return self.back(req)

            user.author_id = author.id
            changes['author'] = author
        else:
            user.author_id = None

        user.lastName = req.args.get('last')
        if not user.lastName:
            add_warning(req, _('Last name required'))
            return self.back(req)

        old_mail = user.mail
        user.mail = req.args.get('email')
        if not user.mail:
            add_warning(req, _('Email address required'))
            return self.back(req)

        if old_mail != user.mail:
            changes['email'] = user.mail
            org_store = CQDEOrganizationStore.instance()
            # TODO: is this correct?
            # When changing email, reset organizations to which the user belongs in
            user.organization_keys = org_store.get_organization_keys(user) or None

        # Update password if changed
        password = req.args.get('password')
        if password:
            if not userstore.is_local(user):
                add_warning(req, _("Can't change password for user that uses external authentication method"))
                return self.back(req)

            if len(password) < 7:
                add_warning(req, _("Password must be at least 7 characters long - please provide longer password"))
                return self.back(req)

            if password != req.args.get('confirmpw'):
                add_warning(req, _("Password do not match - please check"))
                return self.back(req)

        user.givenName = req.args.get('first')
        user.mobile = req.args.get('mobile')

        # Set or reset account expiration date
        expiration_str = req.args.get('expires', '')
        if expiration_str:
            try:
                # Parse date and set expiration time in the end of the day
                expires = datetime.strptime(expiration_str, DATEFORMATS['py'])
                expires += timedelta(hours=23, minutes=59, seconds=59)

                # If changed
                if expires != user.expires:
                    user.expires = expires
                    changes['expiration_date'] = user.expires.strftime(DATEFORMATS['py'])

            except Exception:
                self.log.exception('Date formatting failed')
                add_warning(req, _('Non-recognized expiration format'))
                pass

        # Remove expiration date
        elif user.expires:
            changes['expiration_date'] = 'Never expires'
            user.expires = None

        # Update status if set
        status = int(req.args.get('status', 0))
        if status and status in userstore.USER_STATUS_LABELS.keys() and user.status != status:
            changes['status'] = userstore.USER_STATUS_LABELS[status]
            user.status = status

        if req.args.get('removeicon'):
            user.icon = None
        else:
            icon = req.args.get('icon')
            if not isinstance(icon, unicode) and icon.filename:
                user.createIcon(req.args.get('icon'))

        self.log.info('Saving changes to user: %s' % user)
        ok = userstore.updateUser(user)
        if ok and password:
            changes['password'] = password
            ok = userstore.updatePassword(user, password)

        if not ok:
            add_warning(req, _("Could not save changes"))

        add_notice(req, _("User %s updated" % username))

        # Notify user about changes via email?
        if req.args.get('notify'):
            data = {
                'user':user,
                'changed_by':changed_by,
                'changes':changes
            }

            try:
                enotify = EmailNotifier(self.env, "Account updated", data)
                enotify.template_name = 'account_edited.txt'
                enotify.notify(user.mail)
                add_notice(req, _("Notified user about the changes"))
            except TracError:
                add_warning(req, _("Failed to send email notification - user changed anyway"))

        # Check if user has still (after modification) permission to modify user
        # NOTE: req.perm cannot be used here because it is not updated yet
        resource = Resource('user', id=user.id)
        perm = PermissionCache(self.env, username=req.authname)
        if perm.has_permission('USER_AUTHOR', resource):
            return self.back(req)

        add_notice(req, _('You have no longer permission to modify the account: %s' % user.username))
        return req.redirect(req.href('admin/users/manage'))
Beispiel #8
0
    def render_admin_panel(self, req, cat, page, path_info):

        add_script(req, "multiproject/js/jquery-ui.js")
        add_script(req, "multiproject/js/permissions.js")
        add_stylesheet(req, "multiproject/css/jquery-ui.css")
        add_stylesheet(req, "multiproject/css/permissions.css")

        is_normal_project = self.env.project_identifier != self.env.config.get("multiproject", "sys_home_project_name")

        # API instances
        perm_sys = PermissionSystem(self.env)
        group_store = CQDEUserGroupStore(env=self.env)
        org_store = CQDEOrganizationStore.instance()
        if is_normal_project:
            membership = MembershipApi(self.env, Project.get(self.env))
        else:
            membership = None

        if req.method == "POST":
            action = req.args.get("action")
            if action == "remove_member":
                self._remove_member(req, group_store)
            elif action == "add_member":
                add_type = req.args.get("add_type")
                if add_type == "user":
                    self._add_user(req, group_store, membership)
                elif add_type == "organization":
                    self._add_organization(req, group_store)
                elif add_type == "ldap_group":
                    self._add_ldap_group(req, group_store)
                elif add_type == "login_status":
                    login_status = req.args.get("login_status")
                    if login_status not in ("authenticated", "anonymous"):
                        raise TracError("Invalid arguments")
                    self._add_user(req, group_store, membership, username=login_status)
                else:
                    raise TracError("Invalid add_type")
            elif action == "add_permission":
                self._add_perm_to_group(req, group_store, perm_sys)
            elif action == "remove_permission":
                self._remove_permission(req, group_store, perm_sys)
            elif action == "create_group":
                self._create_group(req, group_store, perm_sys)
            elif action == "remove_group":
                self._remove_group(req, group_store)
            elif action == "add_organization":
                self._add_organization(req, group_store)
            elif action == "decline_membership":
                self._decline_membership(req, membership)
            else:
                raise TracError("Unknown action %s" % action)

        # get membership request list after form posts have been processed
        if is_normal_project:
            membership_requests = set(membership.get_membership_requests())
        else:
            membership_requests = set()

        permissions = set(perm_sys.get_actions())

        # check if project if current configuration and permission state is in such state that
        # permission editions are likely fail
        invalid_state = None
        try:
            group_store.is_valid_group_members()
        except InvalidPermissionsState, e:
            add_warning(
                req,
                _(
                    "Application permission configuration conflicts with project permissions. "
                    "Before you can fully edit permissions or users you will need to either remove "
                    "offending permissions or set correct application configuration. Page reload"
                    "is required to update this warning."
                ),
            )
            add_warning(req, e.message)
    def render_admin_panel(self, req, cat, page, path_info):

        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/permissions.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        add_stylesheet(req, 'multiproject/css/permissions.css')
        
        project = Project.get(self.env) #
        is_normal_project = self.env.project_identifier != \
                            self.env.config.get('multiproject', 'sys_home_project_name')

        # API instances
        perm_sys = PermissionSystem(self.env)
        group_store = CQDEUserGroupStore(env=self.env)
        org_store = CQDEOrganizationStore.instance()
        if is_normal_project:
            membership = MembershipApi(self.env, Project.get(self.env))
        else:
            membership = None

        if req.method == 'POST':
            action = req.args.get('action')
            if action == 'remove_member':
                self._remove_member(req, group_store)
            elif action == 'add_member':
                add_type = req.args.get('add_type')
                if add_type == 'user':
                    self._add_user(req, group_store, membership)
                elif add_type == 'organization':
                    self._add_organization(req, group_store)
                elif add_type == 'ldap_group':
                    self._add_ldap_group(req, group_store)
                elif add_type == 'login_status':
                    login_status = req.args.get('login_status')
                    if login_status not in ('authenticated', 'anonymous'):
                        raise TracError('Invalid arguments')
                    self._add_user(req, group_store, membership, username=login_status)
                else:
                    raise TracError('Invalid add_type')
            elif action == 'add_permission':
                self._add_perm_to_group(req, group_store, perm_sys)
            elif action == 'remove_permission':
                self._remove_permission(req, group_store, perm_sys)
            elif action == 'create_group':
                self._create_group(req, group_store, perm_sys)
            elif action == 'remove_group':
                self._remove_group(req, group_store)
            elif action == 'add_organization':
                self._add_organization(req, group_store)
            elif action == 'decline_membership':
                self._decline_membership(req, membership)
            elif 'makepublic' in req.args:
                project_api = Projects()
                if conf.allow_public_projects:
                    self._make_public(req, project)
                    project_api.add_public_project_visibility(project.id)
                    # Reload page
                    return req.redirect(req.href(req.path_info))
                else:
                    raise TracError("Public projects are disabled", "Error!")
            elif 'makeprivate' in req.args:
                project_api = Projects()
                self._make_private(req, project)
                project_api.remove_public_project_visibility(project.id)
                # Reload page
                return req.redirect(req.href(req.path_info))
            else:
                raise TracError('Unknown action %s' % action)

        # get membership request list after form posts have been processed
        if is_normal_project:
            membership_requests = set(membership.get_membership_requests())
        else:
            membership_requests = set()

        permissions = set(perm_sys.get_actions())

        # check if project if current configuration and permission state is in such state that
        # permission editions are likely fail
        invalid_state = None

        if is_normal_project:
            is_a_public = project.public
        else:
            is_a_public = ""


        try:
            group_store.is_valid_group_members()
        except InvalidPermissionsState, e:
            add_warning(req, _('Application permission configuration conflicts with project permissions. '
                               'Before you can fully edit permissions or users you will need to either remove '
                               'offending permissions or set correct application configuration. Page reload'
                               'is required to update this warning.'))
            add_warning(req, e.message)
Beispiel #10
0
    def edit_user(self, req):
        """
        Handle user edit: view & save
        """
        changes = {}

        username = req.args.get('username')
        if not username:
            add_warning(req, _('Invalid username'))
            return self.list_users(req)

        # Load user being edited
        userstore = get_userstore()
        user = userstore.getUser(username)
        if not user:
            add_warning(req, _('Invalid username (or connection error)'))
            return self.list_users(req)

        # Load user who's doing the edit
        changed_by = userstore.getUser(req.authname)
        papi = Projects()

        # Check permissions and redirect to user listing (handy after editing the user)
        #req.perm.require('USER_AUTHOR', Resource('user', id=user.id))
        if self.check_author_and_deputies(changed_by.id,
            user.author_id, userstore.get_deputies(user.id), req, user.id) == False:
            add_warning(req, _("You don't have rights to edit user"))
            req.redirect(req.href("admin"))

        data = req.args
        data['user'] = user
        data['author'] = userstore.getUserWhereId(user.author_id) if user.author_id else None
        data['deputies'] = userstore.get_deputies(user.id)
        data['base_path'] = req.base_path
        data['dateformats'] = DATEFORMATS
        data['is_local'] = userstore.is_local(user)
        data['now'] = datetime.utcnow()
        data['expired'] = user.expires and ((user.expires - datetime.utcnow()).days < 0)
        data['states'] = userstore.USER_STATUS_LABELS
        data['projects'] = papi.get_authored_projects(user)

        # Add javascript libraries for datepicker and autocomplete
        add_script(req, 'multiproject/js/jquery-ui.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        add_script(req, 'multiproject/js/multiproject.js')
        add_script(req, 'multiproject/js/admin_user_edit.js')

        # If get request show edit
        if req.method.upper() == 'GET' and req.args.get('remove_deputy'):
            deputy = userstore.getUser(req.args.get('remove_deputy').strip())
            remove_res = userstore.remove_deputy(user.id, deputy.id)
            return req.send(remove_res, content_type='text/plain', status=200)
        elif req.method.upper() == 'GET':
            return 'admin_user_edit.html', data

        # Close pressed: get back to user listing
        if req.args.get('close'):
            return req.redirect(req.href('admin/users/manage'))

        if req.args.get('deputy_name'):
            deputy = userstore.getUser(req.args.get('deputy_name').strip())
            resource = Resource('user', id=deputy.id)
            perm = PermissionCache(self.env, username=deputy.username)
            if perm.has_permission('USER_AUTHOR', resource):
                if(userstore.add_deputy(user.id, deputy.username)):
                    add_notice(req, _("Deputy "+deputy.username+" added."))
                    return_url = 'home/admin/users/manage?username='******'home/admin/users/manage?username='******'t have enough rights"))
                return_url = 'home/admin/users/manage?username='******'limitexceeded' in req.args:
            add_warning(req, _('Picture you tried to upload was too big. Try a smaller one'))

        # Update author if changed
        author_id = req.args.get('author_id', None)

        # If id field is empty but name is not: manual input
        if not author_id and req.args.get('author_text'):
            add_warning(req, _('Author cannot be found'))
            return 'admin_user_edit.html', data

        # Check set reset the author
        if author_id:
            author = userstore.getUserWhereId(int(author_id))
            if not author:
                add_warning(req, _('Author cannot be found'))
                return 'admin_user_edit.html', data

            # Check if author is valid: has permission to author?
            perm = PermissionCache(self.env, author.username)
            if 'USER_AUTHOR' not in perm:
                add_warning(req, _('User %s cannot work as an author (does not have USER_AUTHOR permissions)' % author))
                return self.back(req)

            user.author_id = author.id
            changes['author'] = author
        else:
            user.author_id = None

        user.lastName = req.args.get('last')
        if not user.lastName:
            add_warning(req, _('Last name required'))
            return self.back(req)

        old_mail = user.mail
        user.mail = req.args.get('email')
        if not user.mail:
            add_warning(req, _('Email address required'))
            return self.back(req)

        if old_mail != user.mail:
            changes['email'] = user.mail
            org_store = CQDEOrganizationStore.instance()
            # TODO: is this correct?
            # When changing email, reset organizations to which the user belongs in
            user.organization_keys = org_store.get_organization_keys(user) or None

        # Update password if changed
        password = req.args.get('password')
        if password:
            if not userstore.is_local(user):
                add_warning(req, _("Can't change password for user that uses external authentication method"))
                return self.back(req)

            if len(password) < 7:
                add_warning(req, _("Password must be at least 7 characters long - please provide longer password"))
                return self.back(req)

            if password != req.args.get('confirmpw'):
                add_warning(req, _("Password do not match - please check"))
                return self.back(req)

        user.givenName = req.args.get('first')
        user.mobile = req.args.get('mobile')

        # Set or reset account expiration date
        expiration_str = req.args.get('expires', '')
        if expiration_str:
            try:
                # Parse date and set expiration time in the end of the day
                expires = datetime.strptime(expiration_str, DATEFORMATS['py'])
                expires += timedelta(hours=23, minutes=59, seconds=59)

                # If changed
                if expires != user.expires:
                    user.expires = expires
                    changes['expiration_date'] = user.expires.strftime(DATEFORMATS['py'])

            except Exception:
                self.log.exception('Date formatting failed')
                add_warning(req, _('Non-recognized expiration format'))
                pass

        # Remove expiration date
        elif user.expires:
            changes['expiration_date'] = 'Never expires'
            user.expires = None

        # Update status if set
        status = int(req.args.get('status', 0))
        if status and status in userstore.USER_STATUS_LABELS.keys() and user.status != status:
            changes['status'] = userstore.USER_STATUS_LABELS[status]
            user.status = status

        if req.args.get('removeicon'):
            user.icon = None
        else:
            icon = req.args.get('icon')
            if not isinstance(icon, unicode) and icon.filename:
                user.createIcon(req.args.get('icon'))

        self.log.info('Saving changes to user: %s' % user)
        ok = userstore.updateUser(user)
        if ok and password:
            changes['password'] = password
            ok = userstore.updatePassword(user, password)

        if not ok:
            add_warning(req, _("Could not save changes"))

        add_notice(req, _("User %s updated" % username))

        # Notify user about changes via email?
        if req.args.get('notify'):
            data = {
                'user':user,
                'changed_by':changed_by,
                'changes':changes
            }

            try:
                enotify = EmailNotifier(self.env, "Account updated", data)
                enotify.template_name = 'account_edited.txt'
                enotify.notify(user.mail)
                add_notice(req, _("Notified user about the changes"))
            except TracError:
                add_warning(req, _("Failed to send email notification - user changed anyway"))

        # Check if user has still (after modification) permission to modify user
        # NOTE: req.perm cannot be used here because it is not updated yet
        #resource = Resource('user', id=user.id)
        #perm = PermissionCache(self.env, username=req.authname)
        #if perm.has_permission('USER_AUTHOR', resource):
        #    return self.back(req)
        if self.check_author_and_deputies(changed_by.id,
            user.author_id, userstore.get_deputies(user.id), req, user.id) == True:
            return_url = 'home/admin/users/manage?username='******'You have no longer permission to modify the account: %s' % user.username))
            return req.redirect(req.href('admin/users/manage'))
    def render_admin_panel(self, req, cat, page, path_info):

        add_script(req, 'multiproject/js/jquery-ui.js')
        add_script(req, 'multiproject/js/permissions.js')
        add_stylesheet(req, 'multiproject/css/jquery-ui.css')
        add_stylesheet(req, 'multiproject/css/permissions.css')

        project = Project.get(self.env)  #
        is_normal_project = self.env.project_identifier != \
                            self.env.config.get('multiproject', 'sys_home_project_name')

        # API instances
        perm_sys = PermissionSystem(self.env)
        group_store = CQDEUserGroupStore(env=self.env)
        org_store = CQDEOrganizationStore.instance()
        if is_normal_project:
            membership = MembershipApi(self.env, Project.get(self.env))
        else:
            membership = None

        if req.method == 'POST':
            action = req.args.get('action')
            if action == 'remove_member':
                self._remove_member(req, group_store)
            elif action == 'add_member':
                add_type = req.args.get('add_type')
                if add_type == 'user':
                    self._add_user(req, group_store, membership)
                elif add_type == 'organization':
                    self._add_organization(req, group_store)
                elif add_type == 'ldap_group':
                    self._add_ldap_group(req, group_store)
                elif add_type == 'login_status':
                    login_status = req.args.get('login_status')
                    if login_status not in ('authenticated', 'anonymous'):
                        raise TracError('Invalid arguments')
                    self._add_user(req,
                                   group_store,
                                   membership,
                                   username=login_status)
                else:
                    raise TracError('Invalid add_type')
            elif action == 'add_permission':
                self._add_perm_to_group(req, group_store, perm_sys)
            elif action == 'remove_permission':
                self._remove_permission(req, group_store, perm_sys)
            elif action == 'create_group':
                self._create_group(req, group_store, perm_sys)
            elif action == 'remove_group':
                self._remove_group(req, group_store)
            elif action == 'add_organization':
                self._add_organization(req, group_store)
            elif action == 'decline_membership':
                self._decline_membership(req, membership)
            elif 'makepublic' in req.args:
                project_api = Projects()
                if conf.allow_public_projects:
                    self._make_public(req, project)
                    project_api.add_public_project_visibility(project.id)
                    # Reload page
                    return req.redirect(req.href(req.path_info))
                else:
                    raise TracError("Public projects are disabled", "Error!")
            elif 'makeprivate' in req.args:
                project_api = Projects()
                self._make_private(req, project)
                project_api.remove_public_project_visibility(project.id)
                # Reload page
                return req.redirect(req.href(req.path_info))
            else:
                raise TracError('Unknown action %s' % action)

        # get membership request list after form posts have been processed
        if is_normal_project:
            membership_requests = set(membership.get_membership_requests())
        else:
            membership_requests = set()

        permissions = set(perm_sys.get_actions())

        # check if project if current configuration and permission state is in such state that
        # permission editions are likely fail
        invalid_state = None

        if is_normal_project:
            is_a_public = project.public
        else:
            is_a_public = ""

        try:
            group_store.is_valid_group_members()
        except InvalidPermissionsState, e:
            add_warning(
                req,
                _('Application permission configuration conflicts with project permissions. '
                  'Before you can fully edit permissions or users you will need to either remove '
                  'offending permissions or set correct application configuration. Page reload'
                  'is required to update this warning.'))
            add_warning(req, e.message)