예제 #1
0
def _check_permissions(file_info, info):
    """Check access permission on a file.

       _check_permissions(file_info, info)

    Returns a string: empty if access granted or a "denied access" page
    otherwise.
    """
    import aux
    import manage_users
    gids = manage_users._info(info['login_name'])['gids']
    denied_page = ''
    info['title'] = 'File unavailable'
    info['details'] = 'You no longer have access to this file.'
    info['status_button_1'] = """
        <form action="../file_manager.py/process?start" method="post">
        <input type="hidden" name="token" value="%s" />
        <input type="submit" value="Files" />
        </form>
""" % info['token']
    info['status_button_2'] = ''
    info['class'] = 'warning'
    if not file_info:
        denied_page = aux._fill_page(info['status_page_'], info)
    elif not info['uid'] == file_info['owner_uid'] and \
            not file_info['local_share'] and \
            not info['uid'] in file_info['uid_shares'] and \
            not set(gids).intersection(set(file_info['gid_shares'])):
        denied_page = aux._fill_page(info['status_page_'], info)
    return denied_page
예제 #2
0
def _confirm(req, info):
    import logging
    import manage_kbasix
    import manage_users
    import aux
    logging.debug('Starting confirmation')
    info['title'] = aux._fill_str(info['confirmation_title'], info)
    info['status_button_1'] = ''
    info['status_button_2'] = ''
    if 'token' in req.form:
        token = req.form['token']
    else:
        token = ''
    session = manage_kbasix._check_token(token, first_time=True)
    if not session:
        info['class'] = 'fail'
        info[
            'details'] = 'Failed to confirm account due to an invalid or stale token (probably the account confirmation deadline has passed)'
        logging.warn(info['details'] + ' [%s]' % token)
        return aux._fill_page(info['status_page_'], info)
    else:
        session['login_name'] = manage_users._info(
            session['uid'])['login_name']
        info.update(session)
    manage_users._mod(info['login_name'], {'locked': False})
    manage_kbasix._delete_token(info['uid'])
    profile = manage_kbasix._account_info(info['login_name'], 'profile')
    info.update(profile)
    info['class'] = 'success'
    info['details'] = aux._fill_str(info['successful_confirmation_blurb'],
                                    info)
    logging.info('Successfully confirmed account "%s"' % info['login_name'])
    return aux._fill_page(info['status_page_'], info)
예제 #3
0
def _check_permissions(file_info, info):
    """Check access permission on a file.

       _check_permissions(file_info, info)

    Returns a string: empty if access granted or a "denied access" page
    otherwise.
    """
    import aux
    import manage_users
    gids = manage_users._info(info['login_name'])['gids']
    denied_page = ''
    info['title'] = 'File unavailable'
    info['details'] = 'You no longer have access to this file.'
    info['status_button_1'] = """
        <form action="../file_manager.py/process?start" method="post">
        <input type="hidden" name="token" value="%s" />
        <input type="submit" value="Files" />
        </form>
""" % info['token']
    info['status_button_2'] = ''
    info['class'] = 'warning'
    if not file_info:
        denied_page = aux._fill_page(info['status_page_'], info)
    elif not info['uid'] == file_info['owner_uid'] and \
            not file_info['local_share'] and \
            not info['uid'] in file_info['uid_shares'] and \
            not set(gids).intersection(set(file_info['gid_shares'])):
        denied_page = aux._fill_page(info['status_page_'], info)
    return denied_page
예제 #4
0
def process(req):
    from aux import _make_header, _fill_page
    from defs import kbasix, confirm
    info = {}
    info.update(kbasix)
    info.update(confirm)
    import logging
    logging.basicConfig(level = getattr(logging, info['log_level_'].upper()), \
                            filename = info['log_file_'], \
                            datefmt = info['log_dateformat_'], \
                            format = info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for confirm.py')
        info['details'] = '[SYS] Invalid request [%s].' % info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    if not req.is_https():
        info['details'] = 'You cannot confirm over an insecure connection.'
        logging.warn(info['details'])
        return _fill_page(info['error_page_'], info)
    info['token'] = ''
    info['main_header'] = _make_header(info)
    if 'start' in req.form:
        try:
            return _confirm(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = 'Unable to process confirmation [%s].' % info[
                'error_blurb_']
            return _fill_page(info['error_page_'], info)
    else:
        info['details'] = 'Unexpected action trying to confirm registration.'
        logging.warn(info['details'])
        return _fill_page(info['error_page_'], info)
예제 #5
0
def _send_token(req, info):
    """Send an email token if the password was forgotten.

       _send_token(req, info)

    Returns a status page.
    """
    import logging
    import os
    import manage_kbasix
    import manage_users
    import smtplib
    import aux
    from email.mime.text import MIMEText
    logging.debug('Starting forgotten password procedure for "%s"' % \
                      info['login_name'])
    if not manage_users._info(info['login_name']) or \
            manage_users._info(info['login_name'])['locked'] or \
            info['login_name'] in info['banned_logins']:
        info['class'] = 'fail'
        info['main_header'] = aux._make_header(info)
        info['title'] = 'Password reset'
        info['details'] = 'Either that user name was not found, you are \
not allowed into the system, or the account has yet to be activated.'
        info['status_button_1'] = ''
        info['status_button_2'] = ''
        logging.debug('Token not sent to "%s" because "%s"' % \
                          (info['login_name'], info['details']))
        return aux._fill_page(info['status_page_'], info)
    uid = manage_users._info(info['login_name'])['uid']
    user_email = manage_kbasix._account_info(info['login_name'], \
                                                 'profile')['user_email']
    # The token will only allow access to the profile module (which allows
    # to change the password). It should work from any IP address.
    token = manage_kbasix._create_token(req, uid, ip_set=False, \
                                            access='profile.py')
    www_path = os.path.dirname((req.subprocess_env['SCRIPT_NAME']))
    info['profile_url'] = \
        req.construct_url(www_path + \
                              '/profile.py/process?start&token=%s' % token)
    msg = MIMEText(aux._fill_str(info['reset_password_notice'], info))
    msg['Subject'] = aux._fill_str(info['reset_password_subject'], info)
    msg['From'] = info['reset_password_from_email_']
    msg['To'] = user_email
    s = smtplib.SMTP(info['smtp_host_'])
    s.sendmail(info['reset_password_from_email_'], [user_email], \
                   msg.as_string())
    s.quit()
    info['class'] = 'success'
    info['main_header'] = aux._make_header(info)
    info['title'] = 'Password reset'
    info['details'] = aux._fill_str(info['reset_password_blurb'], info)
    info['status_button_1'] = ''
    info['status_button_2'] = ''
    logging.info('Token sent due to forgotten password by "%s" to "%s"' % \
                     (info['login_name'], user_email))
    return aux._fill_page(info['status_page_'], info)
예제 #6
0
def process(req):
    """Process the welcome page.

       process(req)
    """
    # We import functions "as needed" at this stage, making
    # sure they are private (underscore prefix). The exception
    # is "defs", which are just a bunch of defintions (although
    # any functions defined within it should be made private).
    from manage_kbasix import _is_session, _account_info
    from aux import _make_header, _fill_page
    from defs import kbasix, main
    # Here and in all other modules the module-specific definitions
    # take precedence over the 'kbasix' ones.
    info = {}
    info.update(kbasix)
    info.update(main)
    import logging
    logging.basicConfig(level=getattr(logging, \
                                          info['log_level_'].upper()), \
                            filename=info['log_file_'], \
                            datefmt=info['log_dateformat_'], \
                            format=info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for main.py')
        info['details'] = '[SYS] Invalid request [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    try:
        session = _is_session(req, required=False)
        info.update(session)
        if session['token']:
            profile = _account_info(info['login_name'], 'profile')
            info.update(profile)
    except Exception as reason:
        # We use 'warn' level because the '_is_session' redirect seems to
        # trigger this (if 'required=True') although it's not a critical
        # error.
        logging.warn(reason)
        info['details'] = '[SYS] Unable to verify session [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    info['main_header'] = _make_header(info)
    try:
        return _initialize(info)
    except Exception as reason:
        logging.critical(reason)
        info['details'] = '[SYS] Unable to initialize the main \
page [%s].' % info['error_blurb_']
        return _fill_page(info['error_page_'], info)
예제 #7
0
def process(req):
    """Process the logout page.

       process(req)
    """
    from manage_kbasix import _is_session, _account_info
    from aux import _make_header, _fill_page
    from defs import kbasix, logout
    info = {}
    info.update(kbasix)
    info.update(logout)
    import logging
    logging.basicConfig(level = getattr(logging, \
                                            info['log_level_'].upper()), \
                            filename = info['log_file_'], \
                            datefmt = info['log_dateformat_'], \
                            format = info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for logout.py')
        info['details'] = '[SYS] Invalid request [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    try:
        session = _is_session(req, required=False)
        info.update(session)
        if session['token']:
            profile = _account_info(info['login_name'], 'profile')
            info.update(profile)
    except Exception as reason:
        logging.warn(reason)
        info['details'] = '[SYS] Unable to verify session [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    info['main_header'] = _make_header(info)
    if not info['token']:
        info['title'] = 'Logout'
        info['class'] = 'information'
        info['details'] = 'You are not logged in.'
        info['status_button_1'] = ''
        info['status_button_2'] = ''
        return _fill_page(info['status_page_'], info)
    else:
        try:
            return _logout(info['token'], info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to terminate session [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
예제 #8
0
파일: logout.py 프로젝트: KBmarco/KBasix
def process(req):
    """Process the logout page.

       process(req)
    """
    from manage_kbasix import _is_session, _account_info
    from aux import _make_header, _fill_page
    from defs import kbasix, logout
    info = {}
    info.update(kbasix)
    info.update(logout)
    import logging
    logging.basicConfig(level = getattr(logging, \
                                            info['log_level_'].upper()), \
                            filename = info['log_file_'], \
                            datefmt = info['log_dateformat_'], \
                            format = info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for logout.py')
        info['details'] = '[SYS] Invalid request [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    try:
        session = _is_session(req, required=False)
        info.update(session)
        if session['token']:
            profile = _account_info(info['login_name'], 'profile')
            info.update(profile)
    except Exception as reason:
        logging.warn(reason)
        info['details'] = '[SYS] Unable to verify session [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    info['main_header'] = _make_header(info)
    if not info['token']:
        info['title'] = 'Logout'
        info['class'] = 'information'
        info['details'] = 'You are not logged in.'
        info['status_button_1'] = ''
        info['status_button_2'] = ''
        return _fill_page(info['status_page_'], info)
    else:
        try:
            return _logout(info['token'], info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to terminate session [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
예제 #9
0
def _check_quota(req, up_type, info):
    """Check the user's quota. 'up_type' is a string such as 'upload'
    or 'copy', depending on how the file is being added. Only '-file'
    files are counted towards quota.

       (user_dir_size, status) = _check_quota(req, up_type, info)

    Returns an (int, str) tuple with the user's space usage and
    an empty string if underquota, the number -1 and a "you are
    over quota" page (as a string) if overquota. Note that the quota
    doesn't impede a user from going over it, it just disallows
    adding files after it has been exceeded. Worst case scenario
    uses up approximately 'quota + max upload size'.
    """
    import aux
    import logging
    user_dir_size = aux._get_dir_size(info)
    logging.debug('Space usage: %s/%s (%s)' % \
                      (user_dir_size, info['quota'], info['login_name']))
    if user_dir_size >= info['quota']:
        info['user_dir_size'] = user_dir_size
        info['details'] = aux._fill_str(info['quota_limit_blurb'], info)
        info['class'] = 'fail'
        info['status_button_1'] = aux._go_back_button(req, info['token'])
        info['status_button_2'] = ''
        logging.info('File %s forbidden due to quota limits (%s)' % \
                         (up_type, info['login_name']))
        info['title'] = up_type.capitalize()
        return (-1, aux._fill_page(info['status_page_'], info))
    else:
        return (user_dir_size, '')
예제 #10
0
파일: profile.py 프로젝트: KBmarco/KBasix
def _initialize(info):
    import logging
    import manage_kbasix
    import manage_users
    import aux
    profile = manage_kbasix._account_info(info['login_name'], 'profile')
    if '*' in info['allowed_internal_logins'] or \
            info['login_name'] in info['allowed_internal_logins']:
        info['internal_disabled'] = ''
    else:
        info['internal_disabled'] = 'disabled'
    user = manage_users._info(info['login_name'])
    info['first_name'] = user['first_name']
    info['last_name'] = user['last_name']
    if user['auth_method'] == 'ldap':
        info['use_ldap'] = 'checked'
    else:
        info['use_ldap'] = ''
    info['ldap_options'] = ''
    for key in info['ldap_servers']:
        if user['auth_server'] == info['ldap_servers'][key]['server_']:
            info['ldap_servers'][key]['selected_'] = 'selected'
        info['ldap_options'] += info['ldap_servers'][key]['option_'] % info['ldap_servers'][key]
    info['user_ldap_name'] = user['user_auth_name']
    info.update(profile)
    logging.debug('Starting profile page (%s)' % info['login_name'])
    return aux._fill_page(info['profile_page_'], info)
예제 #11
0
def _initialize(info):
    import logging
    import manage_kbasix
    import manage_users
    import aux
    profile = manage_kbasix._account_info(info['login_name'], 'profile')
    if '*' in info['allowed_internal_logins'] or \
            info['login_name'] in info['allowed_internal_logins']:
        info['internal_disabled'] = ''
    else:
        info['internal_disabled'] = 'disabled'
    user = manage_users._info(info['login_name'])
    info['first_name'] = user['first_name']
    info['last_name'] = user['last_name']
    if user['auth_method'] == 'ldap':
        info['use_ldap'] = 'checked'
    else:
        info['use_ldap'] = ''
    info['ldap_options'] = ''
    for key in info['ldap_servers']:
        if user['auth_server'] == info['ldap_servers'][key]['server_']:
            info['ldap_servers'][key]['selected_'] = 'selected'
        info['ldap_options'] += info['ldap_servers'][key]['option_'] % info[
            'ldap_servers'][key]
    info['user_ldap_name'] = user['user_auth_name']
    info.update(profile)
    logging.debug('Starting profile page (%s)' % info['login_name'])
    return aux._fill_page(info['profile_page_'], info)
예제 #12
0
def _initialize(info):
    """Initialize the welcome page.

       _initialize(info)

    Returns the KBasix welcome page.
    """
    import aux
    import logging
    logging.debug('Starting main page (%s)' % info['login_name'])
    return aux._fill_page(info['main_page_'], info)
예제 #13
0
파일: register.py 프로젝트: KBmarco/KBasix
def _initialize(info):
    """Initialize the registration page.

       _initialize(info)

    Returns the KBasix registration page.
    """
    import logging
    import aux
    # Only internal and LDAP authentication are currently supported.
    # Other mechanisms may be added by simply imitating how LDAP is done.
    info['ldap_options'] = ''
    for key in info['ldap_servers']:
        info['ldap_options'] += info['ldap_servers'][key]['option_'] % \
            info['ldap_servers'][key]
    logging.debug('Starting registration page (%s)' % info['login_name'])
    return aux._fill_page(info['register_page_'], info)
예제 #14
0
def _initialize(info):
    """Initialize the registration page.

       _initialize(info)

    Returns the KBasix registration page.
    """
    import logging
    import aux
    # Only internal and LDAP authentication are currently supported.
    # Other mechanisms may be added by simply imitating how LDAP is done.
    info['ldap_options'] = ''
    for key in info['ldap_servers']:
        info['ldap_options'] += info['ldap_servers'][key]['option_'] % \
            info['ldap_servers'][key]
    logging.debug('Starting registration page (%s)' % info['login_name'])
    return aux._fill_page(info['register_page_'], info)
예제 #15
0
def _confirm_delete(req, info):
    """Query for a file deletion confirmation.

       _confirm_delete(req, info)

    Returns a status page.
    """
    import logging
    import aux
    logging.debug('File deletion requested (%s)' % info['login_name'])
    info['file_tag'] = req.form['file_tag'].value
    _check_file_tag(info['file_tag'], info['login_name'])
    file_info = _get_file_info(info['file_tag'], info)
    # There is a check to see whether the file has the
    # appropriate access permissions, but it seems redundant
    # (the file is either deleted now or when the user refreshes
    # the file list). It doesn't hurt to keep, but this check
    # won't be done on bulk deletions, so deleting a single
    # file explicitly or by means of a "single-file-bulk-selection"
    # is slightly different. There's also a slight performance
    # penalty, so the future of the following three lines is not
    # guaranteed.
    denied_page = _check_permissions(file_info, info)
    if denied_page:
        return denied_page
    info['title'] = 'Confirm file deletion'
    info['class'] = 'information'
    info['details'] = 'Really delete file "%s"?' % file_info['file_name']
    info['status_button_1'] = """
          <form action="../file_manager.py/process?action=delete"
            method="post">
            <input type="hidden" name="token" value="%(token)s" />
            <input type="hidden" name="file_tag" value="%(file_tag)s" />
            <input type="submit" value="Delete" />
          </form><br>
""" % info
    info['status_button_2'] = """
          <form action="../file_manager.py/process?start" method="post">
            <input type="hidden" name="token" value="%(token)s" />
            <input type="submit" value="Cancel" />
          </form>
""" % info
    return aux._fill_page(info['status_page_'], info)
예제 #16
0
def _confirm_delete(req, info):
    """Query for a file deletion confirmation.

       _confirm_delete(req, info)

    Returns a status page.
    """
    import logging
    import aux
    logging.debug('File deletion requested (%s)' % info['login_name'])
    info['file_tag'] = req.form['file_tag'].value
    _check_file_tag(info['file_tag'], info['login_name'])
    file_info = _get_file_info(info['file_tag'], info)
    # There is a check to see whether the file has the
    # appropriate access permissions, but it seems redundant
    # (the file is either deleted now or when the user refreshes
    # the file list). It doesn't hurt to keep, but this check
    # won't be done on bulk deletions, so deleting a single
    # file explicitly or by means of a "single-file-bulk-selection"
    # is slightly different. There's also a slight performance
    # penalty, so the future of the following three lines is not
    # guaranteed.
    denied_page = _check_permissions(file_info, info)
    if denied_page:
        return denied_page
    info['title'] = 'Confirm file deletion'
    info['class'] = 'information'
    info['details'] = 'Really delete file "%s"?' % file_info['file_name']
    info['status_button_1'] = """
          <form action="../file_manager.py/process?action=delete"
            method="post">
            <input type="hidden" name="token" value="%(token)s" />
            <input type="hidden" name="file_tag" value="%(file_tag)s" />
            <input type="submit" value="Delete" />
          </form><br>
""" % info
    info['status_button_2'] = """
          <form action="../file_manager.py/process?start" method="post">
            <input type="hidden" name="token" value="%(token)s" />
            <input type="submit" value="Cancel" />
          </form>
""" % info
    return aux._fill_page(info['status_page_'], info)
예제 #17
0
def _logout(token, info):
    """Logout a user.

        _logout(token, info)

    Returns the logout status page.
    """
    import logging
    import manage_kbasix
    import aux
    uid = int(token.split('-')[0])
    manage_kbasix._delete_token(uid, token='*')
    info['token'] = ''
    info['title'] = aux._fill_str(info['goodbye_title'], info)
    info['class'] = 'information'
    info['details'] = aux._fill_str(info['goodbye_blurb'], info)
    info['status_button_1'] = ''
    info['status_button_2'] = ''
    info['main_header'] = aux._make_header(info)
    logging.debug('Successful logout (%s)' % info['login_name'])
    return aux._fill_page(info['status_page_'], info)
예제 #18
0
파일: logout.py 프로젝트: KBmarco/KBasix
def _logout(token, info):
    """Logout a user.

        _logout(token, info)

    Returns the logout status page.
    """
    import logging
    import manage_kbasix
    import aux
    uid = int(token.split('-')[0])
    manage_kbasix._delete_token(uid, token='*')
    info['token'] = ''
    info['title'] = aux._fill_str(info['goodbye_title'], info)
    info['class'] = 'information'
    info['details'] = aux._fill_str(info['goodbye_blurb'], info)
    info['status_button_1'] = ''
    info['status_button_2'] = ''
    info['main_header'] = aux._make_header(info)
    logging.debug('Successful logout (%s)' % info['login_name'])
    return aux._fill_page(info['status_page_'], info)
예제 #19
0
def _initialize(req, info):
    """Initialize the upload page.

       _initialize(req, info)

    Returns the KBasix upload page.
    """
    import logging
    import aux
    info['user_dir_size'] = aux._bytes_string(info['user_dir_size'])
    info['quota'] = aux._bytes_string(info['quota'])
    info['file_types_list'] = '\n'
    # This list of file types is arbitrary, and soley for the benefit
    # of the uploader (to aid the user in file organization). KBasix
    # does support some manner of magic type determination, but the
    # one provided by the user is not used at all for internal purposes.
    for i in info['file_types']:
        info['file_types_list'] += \
            '<option value="%s">%s</option>\n' % (i, i.capitalize())
    logging.debug('Starting upload page (%s)' % info['login_name'])
    return aux._fill_page(info['upload_page_'], info)
예제 #20
0
def _login(req, info):
    """Try to login the user, or send an email token if the password is
    forgotten.

       _login(req, info)

    Returns a status page.
    """
    if 'forgot_password' in req.form:
        try:
            return _send_token(req, info)
        except Exception as reason:
            raise SendTokenError(reason)
    import logging
    from mod_python import apache
    import manage_users
    import manage_kbasix
    import time
    import aux
    from mod_python import util
    logging.debug('Starting login process for "%s"' % info['login_name'])
    if '*' in info['allowed_internal_logins'] or \
            info['login_name'] in info['allowed_internal_logins']:
        allow_internal = True
    else:
        allow_internal = False
    password = req.form['user_password'].value
    # Note that '_authenticate' only returns the uid if 'is_usr' is
    # True, otherwise it'll return a keyword specifying the authentication
    # failure point.
    try:
        (is_usr, status) = manage_users._authenticate(info['login_name'], \
                                                          password)
        logging.info('Authentication for "%s" was "%s" with \
status/uid: %s' % (info['login_name'], is_usr, status))
    except Exception as reason:
        raise LoginError(reason)
    blocked = False
    # We don't just show the reasons for authentication failures, but
    # instead use codes defined in 'defs.py' (the log is explicit in this
    # respect).
    if is_usr:
        uid = status
        locked = manage_users._info(info['login_name'])['locked']
        if locked:
            blocked = True
            msg = info['reason_not_active_']
    else:
        blocked = True
        msg = info['reason_auth_fail_']
    if not blocked:
        auth_method = manage_users._info(info['login_name'])['auth_method']
        # An empty 'auth_method' means the auth is internal.
        if not auth_method and not allow_internal:
            blocked = True
            msg = info['reason_not_allowed_']
        elif info['login_name'] in info['banned_logins']:
            blocked = True
            msg = info['reason_banned_']
    if not blocked:
        try:
            info['token'] = manage_kbasix._create_token(req, uid)
            info['user_name'] = \
                manage_kbasix._account_info(info['login_name'], \
                                                'profile')['user_name']
            info['access'] = 'all'
            info['class'] = 'information'
            info['main_header'] = aux._make_header(info)
            info['title'] = aux._fill_str(info['welcome_title'], info)
            info['details'] = aux._fill_str(info['welcome_blurb'], info)
            info['status_button_1'] = """
<form action="../%(referrer)s/process?start" method="post">
<input type="hidden" name="token" value="%(token)s">
<input type="submit" value="Continue" />
</form>
""" % info
            info['status_button_2'] = ''
            manage_kbasix._account_mod(info['login_name'], \
                                           'profile', {'last_login': \
                                                           time.time()})
            logging.info('Successful login from %s (%s)' % \
                             (req.get_remote_host(apache.REMOTE_NOLOOKUP), \
                                  info['login_name']))
            return aux._fill_page(info['status_page_'], info)
        except Exception as reason:
            raise LoginError(reason)
    else:
        info['class'] = 'fail'
        info['title'] = 'Login'
        info['details'] = aux._fill_str(msg, info)
        info['status_button_1'] = aux._go_back_button(req, token = '')
        info['status_button_2'] = ''
        logging.info('Failed login for "%s" because "%s"' % \
                         (info['login_name'], info['details']))
        return aux._fill_page(info['status_page_'], info)
예제 #21
0
def _initialize(req, info):
    """Initialize the file manager page.

       _initialize(req, info)

    Returns the KBasix file manager page.
    """
    import logging
    import aux
    import cgi
    import manage_kbasix
    logging.debug('Starting the file manager (%s)' % info['login_name'])
    info['user_dir_size'] = aux._bytes_string(aux._get_dir_size(info))
    info['quota'] = aux._bytes_string(info['quota'])
    # Check to see if the files have been bulk-selected.
    if 'toggle_select' in req.form and \
            req.form['toggle_select'].value == 'checked':
        info['toggle_select'] = 'checked'
    else:
        info['toggle_select'] = ''
    # The file manager settings are stored in the user's preferences file.
    prefs = manage_kbasix._account_info(info['login_name'], 'prefs')
    # Default file manager settings.
    if 'file_manager' not in prefs:
        logging.debug('Setting first-time preferences (%s)' % \
                          info['login_name'])
        prefs['file_manager'] = {}
        prefs['file_manager']['hidden_gidloc_shared_files'] = []
        prefs['file_manager']['sort_criteria'] = \
            info['default_sort_criteria_']
        prefs['file_manager']['reverse'] = ''
        prefs['file_manager']['hide_shared'] = ''
        prefs['file_manager']['condensed_view'] = ''
        prefs['file_manager']['keywords'] = ''
    # Update the settings if filtering has been requested.
    for key in prefs['file_manager']:
        if not info['filter']:
            continue
        # This key is not directly changed by editing form values, but by
        # deleting shared entries.
        if key == 'hidden_gidloc_shared_files':
            continue
        if key in req.form:
            # We should use html.escape when migrating to python3
            val = cgi.escape(req.form[key].value, True)
            if key == 'keywords':
                # If the keywords are not decoded here there are problems
                # substituting a mix of unicode and str e.g.:
                #
                #  return '<html><body><p>%s</p><p>%s</p></body></html>' % \
                #   (info['main_header'], prefs['file_manager']['keywords'])
                #
                # fails.
                val = val.decode('utf-8')
            elif val and val \
                    not in info['allowed_sort_criteria'] + ['checked']:
                val = ''
            prefs['file_manager'][key] = val
        # This enables toggling.
        elif key not in req.form and prefs['file_manager'][key]:
            prefs['file_manager'][key] = ''
    # If someone tried to inject an invalid "sort_criteria" it'd be blanked
    # out above, and we fall back on "default_sort_criteria_" (invalid
    # radio buttons are already blanked out).
    if not prefs['file_manager']['sort_criteria']:
        prefs['file_manager']['sort_criteria'] = \
            info['default_sort_criteria_']
    info['sort_criteria_list'] = '\n'
    # Create the sort criteria drop.
    for i in info['allowed_sort_criteria']:
        s = ''
        if i == prefs['file_manager']['sort_criteria']:
            s = 'selected'
        info['sort_criteria_list'] += \
            '<option %s value="%s">%s</option>\n' % \
            (s, i, i.split('_')[-1].capitalize())
    manage_kbasix._account_mod(info['login_name'], 'prefs', prefs)
    info['file_list'] = _get_file_list(info)
    info.update(prefs['file_manager'])
    return aux._fill_page(info['file_manager_page_'], info)
예제 #22
0
파일: register.py 프로젝트: KBmarco/KBasix
def process(req):
    """Process the registration page.

       process(req)
    """
    from manage_kbasix import _is_session, _account_info
    from aux import _make_header, _fill_page
    from defs import kbasix, register
    info = {}
    info.update(kbasix)
    info.update(register)
    import logging
    logging.basicConfig(level = getattr(logging, \
                                            info['log_level_'].upper()), \
                            filename = info['log_file_'], \
                            datefmt = info['log_dateformat_'], \
                            format = info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for register.py')
        info['details'] = '[SYS] Invalid request [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    if not req.is_https():
        info['details'] = 'You cannot register over an insecure connection.'
        logging.info('Disallowed insecure access to register.py')
        return _fill_page(info['error_page_'], info)
    try:
        session = _is_session(req, required=False)
        info.update(session)
        if session['token']:
            profile = _account_info(info['login_name'], 'profile')
            info.update(profile)
    except Exception as reason:
        logging.warn(reason)
        info['details'] = '[SYS] Unable to verify session [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    info['main_header'] = _make_header(info)
    if 'start' in req.form:
        try:
            return _initialize(info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to initialize registration \
[%s].' % info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif 'action' not in req.form:
        info['details'] = 'No action specified trying to register.'
        logging.warn(info['details'])
        return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'register':
        try:
            return _register(req, info)
        except RegisterError as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to register [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to register [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    else:
        info['details'] = 'Unexpected action trying to register.'
        logging.error(info['details'])
        return _fill_page(info['error_page_'], info)
예제 #23
0
파일: register.py 프로젝트: KBmarco/KBasix
def _register(req, info):
    """Perform the registration process.

       _register(req, info)

    Returns the registration status page.
    """
    import logging
    import re
    import time
    import manage_users
    import manage_kbasix
    import aux
    # User names will be unique, case-insensitive and case-aware.
    info['user_name'] = req.form['user_name'].value
    info['login_name'] = info['user_name'].lower()
    logging.info('Registering new account for "%s"' % info['login_name'])
    if '*' in info['allowed_internal_logins'] or \
            info['login_name'] in info['allowed_internal_logins']:
        allow_internal = True
    else:
        allow_internal = False
    info['user_email'] = req.form['user_email'].value
    err = 0
    info['details'] = ''
    # KBasix is not yet tested on different locales, so we enforce
    # ASCII here.
    try:
        info['login_name'].encode('ascii')
        info['user_email'].encode('ascii')
    except:
        err += 1
        info['details'] += 'Your user name and email address must be \
ASCII.<br>'
    if info['login_name'] in info['reserved_login_names']:
        err += 1
        # This might be a lie, but the user need not know that.
        info['details'] += 'That user name is already taken.<br>'
    if info['login_name'] in info['blacklisted_login_names']:
        err += 1
        info['details'] += 'That user name is not permitted.<br>'
    # Keep in mind that, regardless of %(login_name_max_length)s, problems
    # may arise if the file paths are too long (this is an issue with
    # with the fact that the user name is present as part of the "world" 
    # URN). Note that there is a 100 character hard limit, regardless
    # of 'login_name_max_length'.
    if len(info['login_name']) > info['login_name_max_length']:
        err += 1
        info['details'] += 'That user name is too long.<br>'
    if not info['login_name'].isalnum():
        err += 1
        info['details'] += 'Your user name can only contain letters and/or \
numbers.<br>'
    # To avoid UID look-alike names we can set 'alpha_start_login_name'
    if info['alpha_start_login_name'] and len(info['login_name']) > 0 and \
            not info['login_name'][0].isalpha():
        err += 1
        info['details'] += 'The user name must begin with a letter.<br>'
    if len(info['user_email']) > info['email_max_length']:
        err += 1
        info['details'] += 'That email address is too long.<br>'
    if not re.match(info['valid_email_'], info['user_email'], re.I):
        err += 1
        info['details'] += 'Please input a valid email address.<br>'
    if 'use_ldap' in req.form:
        info['user_password'] = '******'
        info['auth_method'] = 'ldap'
        info['ldap_server'] = req.form['ldap_server'].value
        if info['ldap_server']:
            info['user_ldap_name'] = req.form['user_ldap_name'].value
            info['user_ldap_password'] = \
                req.form['user_ldap_password'].value
            (err, info['details']) = _check_ldap(info, err)
        else:
            err += 1
            info['details'] += 'Please specify the LDAP server.<br>'
    elif allow_internal:
        info['user_password'] = req.form['user_password'].value
        info['user_password_check'] = req.form['user_password_check'].value
        info['auth_method'] = ''
        info['ldap_server'] = info['user_ldap_name'] = ''
        (err, info['details']) = _check_password(info, err)
    else:
        err += 1
        info['details'] += 'Unavailable authentication scheme.<br>'
    if not err:
        deadline = time.time() + info['account_confirmation_timeout']
        try:
            (OK, dt) = manage_users._user_add(login_name = \
                                                  info['login_name'], \
                                                  password = \
                                                  info['user_password'], \
                                                  auth_method = \
                                                  info['auth_method'], \
                                                  user_auth_name = \
                                                  info['user_ldap_name'], \
                                                  auth_server = \
                                                  info['ldap_server'], \
                                                  expires = deadline)
            if OK:
                now = time.time()
                profile = {'registered': now, \
                               'login_name': info['login_name'], \
                               'user_name': info['user_name'], \
                               'user_email': info['user_email'], \
                               'quota': info['default_quota'], \
                               'last_login': now}
                manage_kbasix._account_add(info['login_name'], profile)
        except Exception as reason:
            raise RegisterError(reason)
        if not OK:
            err += 1
            info['details'] += dt
        else:
            try:
                _confirmation_email(req, info)
            except:
                err += 1
                info['details'] += 'Unable to send confirmation email.<br>'
    if err:
        info['class'] = 'fail'
        info['status_button_1'] = aux._go_back_button(req, info['token'])
        info['status_button_2'] = ''
        logging.info('Failed account registration for "%s" because "%s"' % \
                         (info['login_name'], \
                              info['details'].replace('<br>',' ')))
    else:
        info['class'] = 'success'
        info['details'] += \
            aux._fill_str(info['successful_registration_blurb'], info)
        info['status_button_1'] = ''
        info['status_button_2'] = ''
        logging.info('Successful account registration for "%s"' % \
                         info['login_name'])
    info['title'] = 'Registration'
    return aux._fill_page(info['status_page_'], info)
예제 #24
0
def process(req):
    """Process the file manager page.

       process(req)
    """
    from manage_kbasix import _is_session, _account_info
    from aux import _make_header, _fill_page
    from defs import kbasix, file_manager
    info = {}
    info.update(kbasix)
    info.update(file_manager)
    import logging
    logging.basicConfig(level = getattr(logging, \
                                            info['log_level_'].upper()), \
                            filename = info['log_file_'], \
                            datefmt = info['log_dateformat_'], \
                            format = info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for file_manager.py')
        info['details'] = '[SYS] Invalid request [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    if not req.is_https():
        info['details'] = 'You cannot manage your file over an insecure \
connection.'

        logging.info('Disallowed insecure access to file_manager.py')
        return _fill_page(info['error_page_'], info)
    try:
        # The holdover is required if "per_request_token" is True, and
        # has no effect otherwise. This is needed due to the
        # client-generated download window (which has no concept of the
        # new request token).
        if 'action' in req.form and req.form['action'] == 'download':
            session = _is_session(req, required=True, holdover=True)
        else:
            session = _is_session(req, required=True)
        info.update(session)
        if session['token']:
            profile = _account_info(info['login_name'], 'profile')
            info.update(profile)
    except Exception as reason:
        logging.warn(reason)
        info['details'] = '[SYS] Unable to verify session [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    info['main_header'] = _make_header(info)
    info['filter'] = False
    if 'start' in req.form:
        try:
            return _initialize(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to initialize the file manager \
[%s].' % info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif 'action' not in req.form:
        info['details'] = 'No action specified within the file manager.'
        logging.warn(info['details'])
        return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'filter':
        try:
            info['filter'] = True
            return _initialize(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] File manager error [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'copy_file':
        try:
            return _copy_file(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] File manager error [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'confirm_delete':
        try:
            return _confirm_delete(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] File manager error [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'delete':
        try:
            return _delete_file(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Error deleting file [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'confirm_bulk_delete':
        try:
            return _confirm_bulk_delete(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Error deleting files [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'download':
        try:
            return _download_file(req, info)
        except Exception as reason:
            # Not critical... user-end problem maybe?
            logging.error(reason)
            info['details'] = '[SYS] Unable to download file [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    else:
        info['details'] = 'Unexpected file manager action.'
        logging.error(info['details'])
        return _fill_page(info['error_page_'], info)
예제 #25
0
def _initialize(req, info):
    """Initialize the metadata editor page.

       _initialize(req, info)

    Returns the KBasix metadata editor page.
    """
    import logging
    import aux
    import file_manager
    file_info = file_manager._get_file_info(info['file_tag'], info)
    # If 'file_info' is empty then that file has been unshared from
    # underneath the user, i.e. they couldn't have edited either way.
    if not file_info or file_info['owner_uid'] != info['uid']:
        info['title'] = 'Metadata unavailable'
        info['details'] = \
            'You cannot edit the metadata of a file you do not own.'
        info['status_button_1'] = """
        <form action="../file_manager.py/process?start" method="post">
        <input type="hidden" name="token" value="%s" />
        <input type="submit" value="Back" />
        </form>
""" % info['token']
        info['status_button_2'] = ''
        info['class'] = 'warning'
        return aux._fill_page(info['status_page_'], info)
    else:
        logging.debug('Starting the metadata editor (%s)' % \
                          info['login_name'])
    info['file_manager_button'] = """
        <form action="../file_manager.py/process?start" method="post">
        <input type="hidden" name="token" value="%s" />
        <input type="submit" value="Files" />
        </form>
""" % info['token']
    entry = {}
    # We need to translate the numeric UIDs and GIDs in actual
    # names. Also, the local and world booleans are converted
    # into their HTML equivalents.
    (entry['uid_shares'], entry['gid_shares'], entry['local_share'], \
         entry['world_share']) = _get_shares(file_info, info)
    # If a file has been shared with the world, i.e. if it's directly
    # available via http the URL is set here.
    if entry['world_share']:
        import manage_kbasix
        import os
        user_name = manage_kbasix._account_info(info['login_name'], \
                                                    'profile')['user_name']
        entry['world_url'] = \
            os.path.join(info['www_url_'], user_name, info['file_tag'])
    else:
        entry['world_url'] = ''
    info['boolean_data'] = aux._fill_str(info['boolean_meta_'], entry)
    # There are four types of metadata:
    #   core metadata: set upon creation e.g. MD5SUM.
    #   basic metadata: non-boolean, user-editable subset of the core
    #                   metadata e.g. UID shares.
    #   boolean metadata: two booleans which toggles between sharing
    #                     with registered users/world.
    #   custom metadata: metadata created by the user.
    # Note that basic is just an arbitrary distinction, whilst custom is
    # stored under a different key (file_info['custom']). Basic metadata
    # is defined by metaeditor['basic_meta'] in defs.py.
    #
    # The 'meta_template_' is the template defined in defs.py which
    # controls the information and layout of the entry.
    info['basic_data'] = ''
    for key, value in file_info.items():
        if key not in info['basic_meta']:
            continue
        data = {}
        data['key'] = key
        # The shares are replaced by the names, not the numeric values.
        if key in ['uid_shares', 'gid_shares']:
            data['value'] = entry[key]
        else:
            data['value'] = value
        data.update(info['basic_meta'][key])
        info['basic_data'] += aux._fill_str(info['meta_template_'], data)
    info['custom_data'] = ''
    for key, value in file_info['custom'].items():
        data = {}
        data['key'] = key
        data['name'] = key
        data['value'] = value
        data['help'] = ''
        info['custom_data'] += aux._fill_str(info['meta_template_'], data)
    info['file_name'] = file_info['file_name']
    return aux._fill_page(info['metaeditor_page_'], info)
예제 #26
0
def process(req):
    """Process the profile page.

       process(req)
    """
    from manage_kbasix import _is_session, _account_info
    from aux import _make_header, _fill_page
    from defs import kbasix, profile
    info = {}
    info.update(kbasix)
    info.update(profile)
    import logging
    logging.basicConfig(level = getattr(logging, \
                                            info['log_level_'].upper()), \
                            filename = info['log_file_'], \
                            datefmt = info['log_dateformat_'], \
                            format = info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for profile.py')
        info['details'] = '[SYS] Invalid request [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    if not req.is_https():
        info['details'] = 'You cannot edit your profile over an insecure \
connection.'

        return _fill_page(info['error_page_'], info)
    try:
        # We need to holdover (which only takes when "per_request_token" is
        # True) because we want to keep the restricted token generated when
        # a password is forgotten. Note that this check should be added to
        # any module which needs to be restricted.
        if 'token' in req.form and '-all-tk' not in req.form['token']:
            session = _is_session(req, required=True, holdover=True)
        else:
            session = _is_session(req, required=True)
        info.update(session)
        if session['token']:
            profile = _account_info(info['login_name'], 'profile')
            info.update(profile)
    except Exception as reason:
        logging.warn(reason)
        info['details'] = '[SYS] Unable to verify session [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    info['main_header'] = _make_header(info)
    if 'start' in req.form:
        try:
            return _initialize(info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to initialize profile editor \
[%s].' % info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif 'action' not in req.form:
        info['details'] = 'No action specified trying to edit profile.'
        logging.warn(info['details'])
        return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'update':
        try:
            return _update(req, info)
        except UpdateError as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to update the profile \
[%s].' % info['error_blurb_']
            return _fill_page(info['error_page_'], info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to update the profile [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    else:
        info['details'] = 'Unexpected action trying to update the profile.'
        logging.error(info['details'])
        return _fill_page(info['error_page_'], info)
예제 #27
0
def _confirm_bulk_delete(req, info):
    """Query for a multiple file deletion confirmation.

       _confirm_bulk_delete(req, info)

    Returns a status page.
    """
    import logging
    import aux
    logging.debug('Bulk file deletion requested (%s)' % info['login_name'])
    info['title'] = 'Confirm the deletion of selected files'
    files = info['file_tags'] = ''
    n = 0
    if 'bulk' not in req.form or not req.form['bulk']:
        info['details'] = 'No files selected'
        info['class'] = 'warning'
        info['status_button_1'] = aux._go_back_button(req, info['token'])
        info['status_button_2'] = ''
        return aux._fill_page(info['status_page_'], info)
    for file_tag in req.form['bulk']:
        # When only one file is selected.
        if not isinstance(req.form['bulk'], list):
            file_tag = req.form['bulk'].value
        _check_file_tag(file_tag, info['login_name'])
        file_info = _get_file_info(file_tag, info)
        # We're not checking permissions on each file (see the
        # comment in '_confirm_delete'). It'd be a hassle to
        # mess up a carefully selected list of files to announce
        # some of them cannot be deleted, even if the final outcome is
        # the same i.e. getting rid of the file(s). In the end the file(s)
        # will not show up once the file managers refreshes, either
        # because they have been deleted or access to them denied.
        if file_info:
            info['file_tags'] += ' ' + file_tag
            files += file_info['file_name'] + '<br>'
            n += 1
        if not isinstance(req.form['bulk'], list): break
    if n == 0:
        # This could happen is one file is selected via checkbox having
        # been unshared from underneath the user.
        logging.warn('No deletable files found (%s)' % info['login_name'])
        info['class'] = 'warning'
        info['details'] = 'No files which you can delete were found.'
        info['status_button_1'] = """
          <form action="../file_manager.py/process?start" method="post">
            <input type="hidden" name="token" value="%(token)s" />
            <input type="submit" value="Back" />
          </form>
""" % info
        info['status_button_2'] = ''
        return aux._fill_page(info['status_page_'], info)
    elif n == 1:
        q = 'file'
    else:
        q = '%s files' % n
    info['details'] = 'Really delete the following %s?<br>%s' % (q, files)
    info['status_button_1'] = """
          <form action="../file_manager.py/process?action=delete"
            method="post">
            <input type="hidden" name="token" value="%(token)s" />
            <input type="hidden" name="file_tags" value="%(file_tags)s" />
            <input type="submit" value="Delete" />
          </form><br>
""" % info
    info['status_button_2'] = """
          <form action="../file_manager.py/process?start" method="post">
            <input type="hidden" name="token" value="%(token)s" />
            <input type="submit" value="Cancel" />
          </form>
""" % info
    info['class'] = 'information'
    return aux._fill_page(info['status_page_'], info)
예제 #28
0
def process(req):
    """Process the file manager page.

       process(req)
    """
    from manage_kbasix import _is_session, _account_info
    from aux import _make_header, _fill_page
    from defs import kbasix, file_manager
    info = {}
    info.update(kbasix)
    info.update(file_manager)
    import logging
    logging.basicConfig(level = getattr(logging, \
                                            info['log_level_'].upper()), \
                            filename = info['log_file_'], \
                            datefmt = info['log_dateformat_'], \
                            format = info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for file_manager.py')
        info['details'] = '[SYS] Invalid request [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    if not req.is_https():
        info['details'] = 'You cannot manage your file over an insecure \
connection.'
        logging.info('Disallowed insecure access to file_manager.py')
        return _fill_page(info['error_page_'], info)
    try:
        # The holdover is required if "per_request_token" is True, and
        # has no effect otherwise. This is needed due to the
        # client-generated download window (which has no concept of the
        # new request token).
        if 'action' in req.form and req.form['action'] == 'download':
            session = _is_session(req, required=True, holdover=True)
        else:
            session = _is_session(req, required=True)
        info.update(session)
        if session['token']:
            profile = _account_info(info['login_name'], 'profile')
            info.update(profile)
    except Exception as reason:
        logging.warn(reason)
        info['details'] = '[SYS] Unable to verify session [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    info['main_header'] = _make_header(info)
    info['filter'] = False
    if 'start' in req.form:
        try:
            return _initialize(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to initialize the file manager \
[%s].' % info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif 'action' not in req.form:
        info['details'] = 'No action specified within the file manager.'
        logging.warn(info['details'])
        return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'filter':
        try:
            info['filter'] = True
            return _initialize(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] File manager error [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'copy_file':
        try:
            return _copy_file(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] File manager error [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'confirm_delete':
        try:
            return _confirm_delete(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] File manager error [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'delete':
        try:
            return _delete_file(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Error deleting file [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'confirm_bulk_delete':
        try:
            return _confirm_bulk_delete(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Error deleting files [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'download':
        try:
            return _download_file(req, info)
        except Exception as reason:
            # Not critical... user-end problem maybe?
            logging.error(reason)
            info['details'] = '[SYS] Unable to download file [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    else:
        info['details'] = 'Unexpected file manager action.'
        logging.error(info['details'])
        return _fill_page(info['error_page_'], info)
예제 #29
0
def _get_file(req, info):    
    """Upload a file (-file) and create its associate metadata (-file-id)
    file.

       _get_file(req, info)

    Returns the KBasix upload status page.
    """
    import os
    import cgi
    import time
    import hashlib
    import json
    import aux
    import logging
    from mod_python import apache
    # Definitions.
    # A time/hash pair is something like this:
    #
    # time 1345131658.602529
    # hash 60f6bee3404d57f04bb47d9369da5b20e4e510eb0ced4011f3de065590407399
    #
    # id and hash are the same string
    # '-file' and '-file-id' are those strings, so that time-hash-file-id
    # is something like:
    #
    #   1345131658.602529-60f6bee3404d...407399-file-id
    #
    # id_tag is time-hash
    # file_tag is time-hash-file
    # (this file_tag is also the name of the actual data file)
    # id_file is time-hash-file-id
    # (this id_file is also the name of the actual metadata file)
    id_tag = '%r-%s' % \
        (time.time(), \
             hashlib.sha256(os.urandom(info['random_length'])).hexdigest())
    file_tag = id_tag + '-file'
    # The user directories are determined by their UID. They are created
    # when the account is created. Remember that UID is always an integer.
    file_out = os.path.join(info['users_root_dir_'], str(info['uid']), \
                                file_tag)
    id_file = file_out + '-id'
    fileitem = req.form['file_name']
    # In order to substitute this value into the message strings it must
    # be decoded.
    file_in = fileitem.filename.decode('utf-8')
    if file_in:
        d = hashlib.md5()
        # Buffer size is set to 2^16, a magic number which seems OK,
        # but should be justified (or changed). Upping to 2**24 didn't
        # seem to make a difference. Note this also has to be changed
        # in the _fbuffer function above.
        # Browsers seem to be able to handle files up to 2GB in size.
        # Uploading a 2082168350b binary file on a 100Mb LAN took about
        # 4 minutes.
        # Progress bars are a no-go (for now?):
        #   http://www.mailinglistarchive.com/[email protected]/msg01880.html
        logging.info('Starting to upload "%s" (%s)' % \
                         (file_out, info['login_name']))
        try:
            f = open(file_out, 'wb', 2**16)
            for chunk in _fbuffer(fileitem.file):
                d.update(chunk)
                f.write(chunk)
        except Exception as reason:
            logging.error('Unable to upload the file type because \
"%s" (%s)' % (reason, info['login_name']))
            raise GetFileError('Upload failed, closing "%s"' % file_out)
        finally:
            f.close()
        os.chmod(file_out, 0600)
        s = os.path.getsize(file_out)
        # You can add to the "id_info" dictionary any other metadata
        # you wish to save, but the following is the basic information
        # all files will contain.
        id_info = {}
        id_info['owner'] = info['login_name']
        id_info['owner_uid'] = info['uid']
        id_info['uid_shares'] = []
        id_info['gid_shares'] = []
        id_info['local_share'] = False
        id_info['world_share'] = False
        id_info['timestamp'] = time.time()
        id_info['file_title'] = req.form['file_title'].value
        id_info['file_description'] = req.form['file_description'].value
        file_type = req.form['file_type'].value
        if file_type not in info['file_types']:
            id_info['file_type'] = 'Unknown'
        else:
            id_info['file_type'] = file_type
        id_info['file_name'] = file_in
        id_info['file_md5sum'] = d.hexdigest()
        id_info['file_size'] = s
        id_info['file_tag'] = file_tag
        # Documentation for python-magic seems non-existent, but it seems
        # to be equivalent to the "file" command (which isn't that great).
        try:
            import magic
            magic_type = magic.open(magic.MAGIC_NONE)
            magic_type.load()
            id_info['magic_type'] = magic_type.file(file_out)
        except Exception as reason:
            logging.error('Unable to determine the file type because \
"%s" (%s)' % (reason, info['login_name']))
            id_info['magic_type'] = 'Unknown'
        id_info['custom'] = {}
        for key in id_info:
            if isinstance(id_info[key], basestring):
                # We should use html.escape when migrating to python3
                id_info[key] = cgi.escape(id_info[key], True)
        try:
            f = open(id_file, 'wb')
            json.dump(id_info, f)
        except Exception as reason:
            logging.error('Unable to create the metadata file because \
"%s" (%s)' % (reason, info['login_name']))
            if os.isfile(file_out):
                os.remove(file_out)
            raise GetFileError('Metadata creation failed, deleted upload \
file "%s" and closing "%s"' % (file_out, id_file))
        finally:
            f.close()
        os.chmod(id_file, 0600)
        n = id_info['file_name']
        if s == 0:
            info['class'] = 'warning'
            info['details'] = 'File "%s" uploaded but empty (it may be \
client-side unreadable)' % n
        else:
            s = aux._bytes_string(s)
            info['class'] = 'success'
            info['details'] = aux._fill_str(info['successful_upload_'], \
                                                {'name': \
                                                     id_info['file_name'], \
                                                     'type': \
                                                     id_info['file_type'], \
                                                     'size': s, \
                                                     'md5': d.hexdigest()})
        logging.info('Finished uploading and saving "%s" (%s)' % \
                         (file_out, info['login_name']))
    else:
        info['class'] = 'fail'
        info['details'] = 'No file was specified.'
    info['title'] = 'File Upload'
    # We don't use the usual "go back" button because it will show the
    # upload animation.
    info['status_button_1'] = """
    <form action="../upload.py/process?start" method="post">
      <input type="hidden" name="token" value="%(token)s" />
      <input type="submit" value="Back" />
    </form>
    """ % info
    info['status_button_2'] = ''
    return aux._fill_page(info['status_page_'], info)
예제 #30
0
def process(req):
    """Process the upload page.

       process(req)
    """
    # Note that upload sizes can be configured via "LimitRequestBody"
    # either in .htaccess or httpd.conf (the latter requires an httpd
    # restart). However, doing so results in mod_python spitting out a
    # nasty error message ("Request Entity Too Large") upon hitting said
    # limit. See:
    #
    #   http://www.mail-archive.com/[email protected]/msg87503.html
    #
    # We can somewhat gracefully bypass this behaviour by adding the
    # following to .htaccess (using 10485760 as an example, and keeping in
    # mind that on a production server PythonDebug should be Off in
    # python.conf):
    #
    #  PythonDebug Off
    #  LimitRequestBody 10485760
    #  ErrorDocument 413 /path/upload.py/process?file_limit=10485760
    #  ErrorDocument 500 "An error was encountered"
    #
    # Note that the ErrorDocument message should be small (see below), and
    # that the paths are relative to DocumentRoot.
    #
    # Now, this approach has the following issue: the file is _not_
    # uploaded to the server, but instead seems to be stored on the client
    # side and for some reason takes a long time to process there
    # (strangely so, as it's not being transferred). This method seems to
    # work well for medium-sized files above LimitRequestBody, but then for
    # huge files the client shows a connection reset error, explained here:
    #
    #   http://stackoverflow.com/questions/4467443/limitrequestbody-doesnt-respond-with-413-for-large-file25mb
    #
    # In spite of the fact that the client feedback is slow (and that the
    # connection is reset on large files) it may be worthwhile to keep
    # since at least this does limit the resources used on the server,
    # including avoiding the "phantom file" space usage on /tmp (downloads
    # are stored in /tmp as a "phantom file", its usage can be obtained via
    # "df", freeing it requires an httpd restart if the upload happens to
    # die in one of the "file too large" manners [just closing the tab does
    # not cause this]).
    #
    # In short:
    #
    #   0                < size <  LimitRequestBody: Upload success ful (OK)
    #   LimitRequestBody < size <  ~1GB            : Upload limit message
    #                                                                   (OK)
    #   ~1GB             < size <  ~2GB            : Connection reset (file
    #                                                             too large)
    #   ~2GB             < size                    : Browsers barf (file
    #                                                             too large)

    from manage_kbasix import _is_session, _account_info
    from aux import _make_header, _fill_page, _fill_str, _go_back_button
    from defs import kbasix, upload
    info = {}
    info.update(kbasix)
    info.update(upload)
    import logging
    logging.basicConfig(level = getattr(logging, \
                                            info['log_level_'].upper()), \
                            filename = info['log_file_'], \
                            datefmt = info['log_dateformat_'], \
                            format = info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for upload.py')
        info['details'] = '[SYS] Invalid request [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)

    # The following 'file_limit' comes from the 413 error redirect in
    # .htaccess. We disallow sneakiness by making sure the only argument
    # is an integer. Furthermore we note that no session handling is done
    # by this point (so, although the error page has no tokens, the browser
    # "Back" button will take us to our upload page, regardless of whether
    # the tokens are per-request). Since it's a dead-end error page with
    # no user-data it shouldn't affect the fact it lives outside a session's
    # scope.

    # Although the uploads page is session-protected the upload-limit error
    # message has to be public as we can't get the token from
    # .htaccess. Furthermore, it seems this message has to be pretty small
    # (returning "status_page_" gives "Request Entity Too Large"). Note
    # that the user temporarily loses their token, but has no other option
    # other than to browse "Back", recovering their token (also important
    # because HTTPS isn't required either).

    if 'file_limit' in req.form:
        file_limit = req.form['file_limit'].value
        try:
            max_size = int(file_limit)
        except:
            info['details'] = 'Non-numerical upload size'
            logging.error(info['details'] + ': %s' % file_limit)
            return _fill_page(info['error_page_'], info)
        info['details'] = 'The file you are trying to upload is too \
large (max size = %s bytes).' % max_size
        logging.warn(info['details'])
        return _fill_page(info['error_page_'], info)
    if not req.is_https():
        info['details'] = 'You cannot upload over an insecure connection.'
        logging.info(info['details'])
        return _fill_page(info['error_page_'], info)
    try:
        session = _is_session(req, required=True)
        info.update(session)
        if session['token']:
            profile = _account_info(info['login_name'], 'profile')
            info.update(profile)
    except Exception as reason:
        logging.warn(reason)
        info['details'] = '[SYS] Unable to verify session [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    info['main_header'] = _make_header(info)
    # User cannot upload if over-quota.
    try:
        (info['user_dir_size'], over_page) = \
            _check_quota(req, 'upload', info)
    except Exception as reason:
        logging.critical(reason)
        info['details'] = '[SYS] Unable to determine quota [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    if info['user_dir_size'] < 0:
        return over_page
    if 'start' in req.form:
        try:
            return _initialize(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to initialize upload [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif 'action' not in req.form:
        info['details'] = 'No action specified trying to upload.'
        logging.warn(info['details'])
        return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'upload_file':
        try:
            return _get_file(req, info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Error uploading file [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    else:
        info['details'] = 'Unexpected action trying to upload.'
        logging.error(info['details'])
        return _fill_page(info['error_page_'], info)
예제 #31
0
def _register(req, info):
    """Perform the registration process.

       _register(req, info)

    Returns the registration status page.
    """
    import logging
    import re
    import time
    import manage_users
    import manage_kbasix
    import aux
    # User names will be unique, case-insensitive and case-aware.
    info['user_name'] = req.form['user_name'].value
    info['login_name'] = info['user_name'].lower()
    logging.info('Registering new account for "%s"' % info['login_name'])
    if '*' in info['allowed_internal_logins'] or \
            info['login_name'] in info['allowed_internal_logins']:
        allow_internal = True
    else:
        allow_internal = False
    info['user_email'] = req.form['user_email'].value
    err = 0
    info['details'] = ''
    # KBasix is not yet tested on different locales, so we enforce
    # ASCII here.
    try:
        info['login_name'].encode('ascii')
        info['user_email'].encode('ascii')
    except:
        err += 1
        info['details'] += 'Your user name and email address must be \
ASCII.<br>'

    if info['login_name'] in info['reserved_login_names']:
        err += 1
        # This might be a lie, but the user need not know that.
        info['details'] += 'That user name is already taken.<br>'
    if info['login_name'] in info['blacklisted_login_names']:
        err += 1
        info['details'] += 'That user name is not permitted.<br>'
    # Keep in mind that, regardless of %(login_name_max_length)s, problems
    # may arise if the file paths are too long (this is an issue with
    # with the fact that the user name is present as part of the "world"
    # URN). Note that there is a 100 character hard limit, regardless
    # of 'login_name_max_length'.
    if len(info['login_name']) > info['login_name_max_length']:
        err += 1
        info['details'] += 'That user name is too long.<br>'
    if not info['login_name'].isalnum():
        err += 1
        info['details'] += 'Your user name can only contain letters and/or \
numbers.<br>'

    # To avoid UID look-alike names we can set 'alpha_start_login_name'
    if info['alpha_start_login_name'] and len(info['login_name']) > 0 and \
            not info['login_name'][0].isalpha():
        err += 1
        info['details'] += 'The user name must begin with a letter.<br>'
    if len(info['user_email']) > info['email_max_length']:
        err += 1
        info['details'] += 'That email address is too long.<br>'
    if not re.match(info['valid_email_'], info['user_email'], re.I):
        err += 1
        info['details'] += 'Please input a valid email address.<br>'
    if 'use_ldap' in req.form:
        info['user_password'] = '******'
        info['auth_method'] = 'ldap'
        info['ldap_server'] = req.form['ldap_server'].value
        if info['ldap_server']:
            info['user_ldap_name'] = req.form['user_ldap_name'].value
            info['user_ldap_password'] = \
                req.form['user_ldap_password'].value
            (err, info['details']) = _check_ldap(info, err)
        else:
            err += 1
            info['details'] += 'Please specify the LDAP server.<br>'
    elif allow_internal:
        info['user_password'] = req.form['user_password'].value
        info['user_password_check'] = req.form['user_password_check'].value
        info['auth_method'] = ''
        info['ldap_server'] = info['user_ldap_name'] = ''
        (err, info['details']) = _check_password(info, err)
    else:
        err += 1
        info['details'] += 'Unavailable authentication scheme.<br>'
    if not err:
        deadline = time.time() + info['account_confirmation_timeout']
        try:
            (OK, dt) = manage_users._user_add(login_name = \
                                                  info['login_name'], \
                                                  password = \
                                                  info['user_password'], \
                                                  auth_method = \
                                                  info['auth_method'], \
                                                  user_auth_name = \
                                                  info['user_ldap_name'], \
                                                  auth_server = \
                                                  info['ldap_server'], \
                                                  expires = deadline)
            if OK:
                now = time.time()
                profile = {'registered': now, \
                               'login_name': info['login_name'], \
                               'user_name': info['user_name'], \
                               'user_email': info['user_email'], \
                               'quota': info['default_quota'], \
                               'last_login': now}
                manage_kbasix._account_add(info['login_name'], profile)
        except Exception as reason:
            raise RegisterError(reason)
        if not OK:
            err += 1
            info['details'] += dt
        else:
            try:
                _confirmation_email(req, info)
            except:
                err += 1
                info['details'] += 'Unable to send confirmation email.<br>'
    if err:
        info['class'] = 'fail'
        info['status_button_1'] = aux._go_back_button(req, info['token'])
        info['status_button_2'] = ''
        logging.info('Failed account registration for "%s" because "%s"' % \
                         (info['login_name'], \
                              info['details'].replace('<br>',' ')))
    else:
        info['class'] = 'success'
        info['details'] += \
            aux._fill_str(info['successful_registration_blurb'], info)
        info['status_button_1'] = ''
        info['status_button_2'] = ''
        logging.info('Successful account registration for "%s"' % \
                         info['login_name'])
    info['title'] = 'Registration'
    return aux._fill_page(info['status_page_'], info)
예제 #32
0
def process(req):
    """Process the login page.

       process(req)
    """
    from manage_kbasix import _is_session, _account_info
    from aux import _make_header, _fill_page
    from defs import kbasix, login
    info = {}
    info.update(kbasix)
    info.update(login)
    import logging
    logging.basicConfig(level = getattr(logging, \
                                            info['log_level_'].upper()), \
                            filename = info['log_file_'], \
                            datefmt = info['log_dateformat_'], \
                            format = info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for login.py')
        info['details'] = '[SYS] Invalid request [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    if not req.is_https():
        info['details'] = 'You cannot login over an insecure connection.'
        logging.info('Disallowed insecure access to login.py')
        return _fill_page(info['error_page_'], info)
    try:
        session = _is_session(req, required=False)
        info.update(session)
        if session['token']:
            profile = _account_info(info['login_name'], 'profile')
            info.update(profile)
    except Exception as reason:
        logging.warn(reason)
        info['details'] = '[SYS] Unable to verify session [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    # This page should not appear to people who are already logged in.
    if info['token']:
        info['details'] = 'You are already logged in as "%s".' % \
            info['user_name']
        logging.debug(info['details'])
        return _fill_page(info['error_page_'], info)
    info['main_header'] = _make_header(info)
    # When a page requires a user to be logged-in it sends its name as
    # "referrer", which is obtained from "req.canonical_filename", and
    # spoofing it shouldn't gain anything other than attempting to access
    # a different URL (which can be done by editing the URL directly
    # anyways).
    if 'referrer' not in req.form:
        info['referrer'] = 'main.py'
    else:
        info['referrer'] = req.form['referrer'].value
    if 'start' in req.form:
        try:
            return _initialize(info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to open login page [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif 'action' not in req.form:
        info['details'] = 'No action specified trying to login.'
        logging.warn(info['details'])
        return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'login':
        try:
            if not req.form['user_name']:
                info['details'] = 'You must input your user name.'
                return _fill_page(info['error_page_'], info)
            else:
                info['user_name'] = req.form['user_name'].value
                info['login_name'] = info['user_name'].lower()
            return _login(req, info)
        except SendTokenError as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to email new token [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
        except LoginError as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to login [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to login [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    else:
        info['details'] = 'Unexpected action trying to login.'
        logging.error(info['details'])
        return _fill_page(info['error_page_'], info)
예제 #33
0
def _initialize(req, info):
    """Initialize the file manager page.

       _initialize(req, info)

    Returns the KBasix file manager page.
    """
    import logging
    import aux
    import cgi
    import manage_kbasix
    logging.debug('Starting the file manager (%s)' % info['login_name'])
    info['user_dir_size'] = aux._bytes_string(aux._get_dir_size(info))
    info['quota'] = aux._bytes_string(info['quota'])
    # Check to see if the files have been bulk-selected.
    if 'toggle_select' in req.form and \
            req.form['toggle_select'].value == 'checked':
        info['toggle_select'] = 'checked'
    else:
        info['toggle_select'] = ''
    # The file manager settings are stored in the user's preferences file.
    prefs = manage_kbasix._account_info(info['login_name'], 'prefs')
    # Default file manager settings.
    if 'file_manager' not in prefs:
        logging.debug('Setting first-time preferences (%s)' % \
                          info['login_name'])
        prefs['file_manager'] = {}
        prefs['file_manager']['hidden_gidloc_shared_files'] = []
        prefs['file_manager']['sort_criteria'] = \
            info['default_sort_criteria_']
        prefs['file_manager']['reverse'] = ''
        prefs['file_manager']['hide_shared'] = ''
        prefs['file_manager']['condensed_view'] = ''
        prefs['file_manager']['keywords'] = ''
    # Update the settings if filtering has been requested.
    for key in prefs['file_manager']:
        if not info['filter']:
            continue
        # This key is not directly changed by editing form values, but by
        # deleting shared entries.
        if key == 'hidden_gidloc_shared_files':
            continue
        if key in req.form:
            # We should use html.escape when migrating to python3
            val = cgi.escape(req.form[key].value, True)
            if key == 'keywords':
                # If the keywords are not decoded here there are problems
                # substituting a mix of unicode and str e.g.:
                #
                #  return '<html><body><p>%s</p><p>%s</p></body></html>' % \
                #   (info['main_header'], prefs['file_manager']['keywords'])
                #
                # fails.
                val = val.decode('utf-8')
            elif val and val \
                    not in info['allowed_sort_criteria'] + ['checked']:
                val = ''
            prefs['file_manager'][key] = val
        # This enables toggling.
        elif key not in req.form and prefs['file_manager'][key]:
            prefs['file_manager'][key] = ''
    # If someone tried to inject an invalid "sort_criteria" it'd be blanked
    # out above, and we fall back on "default_sort_criteria_" (invalid
    # radio buttons are already blanked out).
    if not prefs['file_manager']['sort_criteria']:
        prefs['file_manager']['sort_criteria'] = \
            info['default_sort_criteria_']
    info['sort_criteria_list'] = '\n'
    # Create the sort criteria drop.
    for i in info['allowed_sort_criteria']:
        s = ''
        if i == prefs['file_manager']['sort_criteria']:
            s = 'selected'
        info['sort_criteria_list'] += \
            '<option %s value="%s">%s</option>\n' % \
            (s, i, i.split('_')[-1].capitalize())
    manage_kbasix._account_mod(info['login_name'], 'prefs', prefs)
    info['file_list'] = _get_file_list(info)
    info.update(prefs['file_manager'])
    return aux._fill_page(info['file_manager_page_'], info)
예제 #34
0
def _update(req, info):
    import logging
    import re
    import time
    import manage_users
    import manage_kbasix
    import aux
    if '*' in info['allowed_internal_logins'] or \
            info['login_name'] in info['allowed_internal_logins']:
        allow_internal = True
    else:
        allow_internal = False
    profile = {}
    account = {}
    account['first_name'] = req.form['first_name'].value
    account['last_name'] = req.form['last_name'].value
    profile['user_email'] = req.form['user_email'].value
    err = 0
    info['details'] = ''
    if not re.match(info['valid_email_'], profile['user_email'], re.I):
        err += 1
        info['details'] += 'Please input a valid email address.<br>'
    if 'user_password' in req.form:
        info['user_password'] = req.form['user_password'].value
    else:
        info['user_password'] = ''
    info['user_ldap_password'] = req.form['user_ldap_password'].value
    info['user_ldap_name'] = req.form['user_ldap_name'].value
    was_external = manage_users._info(info['login_name'])['auth_method']
    if 'use_ldap' in req.form and info['user_ldap_password']:
        if not info['user_ldap_name']:
            err += 1
            info['details'] += 'Please fill out all the LDAP credentials.<br>'
        else:
            from register import _check_ldap
            info['ldap_server'] = req.form['ldap_server'].value
            if info['ldap_server']:
                (err, info['details']) = _check_ldap(info, err)
            else:
                err += 1
                info['details'] += 'Please specify the LDAP server.<br>'
            if not err:
                account['password'] = '******'
                account['auth_method'] = 'ldap'
                account['auth_server'] = info['ldap_server']
                account['user_auth_name'] = info['user_ldap_name']
                if not was_external:
                    info[
                        'details'] += 'Authentication is no longer internal.<br>'
    elif info['user_password'] and allow_internal:
        from register import _check_password
        info['user_password_check'] = req.form['user_password_check'].value
        (err, info['details']) = _check_password(info, err)
        if not err:
            account['auth_method'] = ''
            account['auth_server'] = account['user_auth_name'] = ''
            account['password'] = info['user_password']
            if was_external:
                info['details'] += 'Authentication is now internal.<br>'
    else:
        info['details'] += 'Note: no authentication changes made.<br>'
    if not err:
        try:
            manage_kbasix._account_mod(info['login_name'], 'profile', profile)
            manage_users._mod(info['login_name'], account)
        except Exception as reason:
            raise UpdateError(reason)
    if err:
        info['class'] = 'fail'
        info['status_button_1'] = aux._go_back_button(req, info['token'])
        info['status_button_2'] = ''
        logging.debug('Failed profile update because "%s" (%s)' % \
                          (info['details'].replace('<br>',' '), info['login_name']))
    else:
        info['class'] = 'success'
        info['details'] += aux._fill_str(info['successful_update_blurb'], info)
        if info['access'] == 'profile.py':
            info['status_button_1'] = """
          <form action="../login.py/process?start" method="post">
            <input type="submit" value="Login" />
          </form><br>
""" % info
        else:
            info['status_button_1'] = ''
        info['status_button_2'] = ''
        logging.debug('Successful profile update (%s)' % info['login_name'])
    info['title'] = 'User Profile'
    return aux._fill_page(info['status_page_'], info)
예제 #35
0
def _confirm_bulk_delete(req, info):
    """Query for a multiple file deletion confirmation.

       _confirm_bulk_delete(req, info)

    Returns a status page.
    """
    import logging
    import aux
    logging.debug('Bulk file deletion requested (%s)' % info['login_name'])
    info['title'] = 'Confirm the deletion of selected files'
    files = info['file_tags'] = ''
    n = 0
    if 'bulk' not in req.form or not req.form['bulk']:
        info['details'] = 'No files selected'
        info['class'] = 'warning'
        info['status_button_1'] = aux._go_back_button(req, info['token'])
        info['status_button_2'] = ''
        return aux._fill_page(info['status_page_'], info)
    for file_tag in req.form['bulk']:
        # When only one file is selected.
        if not isinstance(req.form['bulk'], list):
            file_tag = req.form['bulk'].value
        _check_file_tag(file_tag, info['login_name'])
        file_info = _get_file_info(file_tag, info)
        # We're not checking permissions on each file (see the
        # comment in '_confirm_delete'). It'd be a hassle to
        # mess up a carefully selected list of files to announce
        # some of them cannot be deleted, even if the final outcome is
        # the same i.e. getting rid of the file(s). In the end the file(s)
        # will not show up once the file managers refreshes, either
        # because they have been deleted or access to them denied.
        if file_info:
            info['file_tags'] += ' ' + file_tag
            files += file_info['file_name'] + '<br>'
            n += 1
        if not isinstance(req.form['bulk'], list): break
    if n == 0:
        # This could happen is one file is selected via checkbox having
        # been unshared from underneath the user.
        logging.warn('No deletable files found (%s)' % info['login_name'])
        info['class'] = 'warning'
        info['details'] = 'No files which you can delete were found.'
        info['status_button_1'] = """
          <form action="../file_manager.py/process?start" method="post">
            <input type="hidden" name="token" value="%(token)s" />
            <input type="submit" value="Back" />
          </form>
""" % info
        info['status_button_2'] = ''
        return aux._fill_page(info['status_page_'], info)
    elif n == 1:
        q = 'file'
    else:
        q = '%s files' % n
    info['details'] = 'Really delete the following %s?<br>%s' % (q, files)
    info['status_button_1'] = """
          <form action="../file_manager.py/process?action=delete"
            method="post">
            <input type="hidden" name="token" value="%(token)s" />
            <input type="hidden" name="file_tags" value="%(file_tags)s" />
            <input type="submit" value="Delete" />
          </form><br>
""" % info
    info['status_button_2'] = """
          <form action="../file_manager.py/process?start" method="post">
            <input type="hidden" name="token" value="%(token)s" />
            <input type="submit" value="Cancel" />
          </form>
""" % info
    info['class'] = 'information'
    return aux._fill_page(info['status_page_'], info)
예제 #36
0
파일: profile.py 프로젝트: KBmarco/KBasix
def process(req):
    """Process the profile page.

       process(req)
    """
    from manage_kbasix import _is_session, _account_info
    from aux import _make_header, _fill_page
    from defs import kbasix, profile
    info = {}
    info.update(kbasix)
    info.update(profile)
    import logging
    logging.basicConfig(level = getattr(logging, \
                                            info['log_level_'].upper()), \
                            filename = info['log_file_'], \
                            datefmt = info['log_dateformat_'], \
                            format = info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for profile.py')
        info['details'] = '[SYS] Invalid request [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    if not req.is_https():
        info['details'] = 'You cannot edit your profile over an insecure \
connection.'
        return _fill_page(info['error_page_'], info)
    try:
        # We need to holdover (which only takes when "per_request_token" is
        # True) because we want to keep the restricted token generated when
        # a password is forgotten. Note that this check should be added to
        # any module which needs to be restricted.
        if 'token' in req.form and '-all-tk' not in req.form['token']:
            session = _is_session(req, required=True, holdover=True)    
        else:
            session = _is_session(req, required=True)
        info.update(session)
        if session['token']:
            profile = _account_info(info['login_name'], 'profile')
            info.update(profile)
    except Exception as reason:
        logging.warn(reason)
        info['details'] = '[SYS] Unable to verify session [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    info['main_header'] = _make_header(info)
    if 'start' in req.form:
        try:
            return _initialize(info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to initialize profile editor \
[%s].' % info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif 'action' not in req.form:
        info['details'] = 'No action specified trying to edit profile.'
        logging.warn(info['details'])
        return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'update':
        try:
            return _update(req, info)
        except UpdateError as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to update the profile \
[%s].' % info['error_blurb_']
            return _fill_page(info['error_page_'], info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to update the profile [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    else:
        info['details'] = 'Unexpected action trying to update the profile.'
        logging.error(info['details'])
        return _fill_page(info['error_page_'], info)
예제 #37
0
def process(req):
    """Process the registration page.

       process(req)
    """
    from manage_kbasix import _is_session, _account_info
    from aux import _make_header, _fill_page
    from defs import kbasix, register
    info = {}
    info.update(kbasix)
    info.update(register)
    import logging
    logging.basicConfig(level = getattr(logging, \
                                            info['log_level_'].upper()), \
                            filename = info['log_file_'], \
                            datefmt = info['log_dateformat_'], \
                            format = info['log_format_'])
    if repr(type(req)) != "<type 'mp_request'>":
        logging.critical('Invalid request for register.py')
        info['details'] = '[SYS] Invalid request [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    if not req.is_https():
        info['details'] = 'You cannot register over an insecure connection.'
        logging.info('Disallowed insecure access to register.py')
        return _fill_page(info['error_page_'], info)
    try:
        session = _is_session(req, required=False)
        info.update(session)
        if session['token']:
            profile = _account_info(info['login_name'], 'profile')
            info.update(profile)
    except Exception as reason:
        logging.warn(reason)
        info['details'] = '[SYS] Unable to verify session [%s].' % \
            info['error_blurb_']
        return _fill_page(info['error_page_'], info)
    info['main_header'] = _make_header(info)
    if 'start' in req.form:
        try:
            return _initialize(info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to initialize registration \
[%s].' % info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    elif 'action' not in req.form:
        info['details'] = 'No action specified trying to register.'
        logging.warn(info['details'])
        return _fill_page(info['error_page_'], info)
    elif req.form['action'] == 'register':
        try:
            return _register(req, info)
        except RegisterError as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to register [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
        except Exception as reason:
            logging.critical(reason)
            info['details'] = '[SYS] Unable to register [%s].' % \
                info['error_blurb_']
            return _fill_page(info['error_page_'], info)
    else:
        info['details'] = 'Unexpected action trying to register.'
        logging.error(info['details'])
        return _fill_page(info['error_page_'], info)
예제 #38
0
파일: profile.py 프로젝트: KBmarco/KBasix
def _update(req, info):
    import logging
    import re
    import time
    import manage_users
    import manage_kbasix
    import aux
    if '*' in info['allowed_internal_logins'] or \
            info['login_name'] in info['allowed_internal_logins']:
        allow_internal = True
    else:
        allow_internal = False
    profile = {}
    account = {}
    account['first_name'] = req.form['first_name'].value
    account['last_name'] = req.form['last_name'].value
    profile['user_email'] = req.form['user_email'].value
    err = 0
    info['details'] = ''
    if not re.match(info['valid_email_'], profile['user_email'], re.I):
        err += 1
        info['details'] += 'Please input a valid email address.<br>'
    if 'user_password' in req.form:
        info['user_password'] = req.form['user_password'].value
    else:
        info['user_password'] = ''
    info['user_ldap_password'] = req.form['user_ldap_password'].value
    info['user_ldap_name'] = req.form['user_ldap_name'].value
    was_external = manage_users._info(info['login_name'])['auth_method']
    if 'use_ldap' in req.form and info['user_ldap_password']:
        if not info['user_ldap_name']:
            err += 1
            info['details'] += 'Please fill out all the LDAP credentials.<br>'
        else:
                from register import _check_ldap
                info['ldap_server'] = req.form['ldap_server'].value
                if info['ldap_server']:
                    (err, info['details']) = _check_ldap(info, err)
                else:
                    err += 1
                    info['details'] += 'Please specify the LDAP server.<br>'
                if not err:
                    account['password'] = '******'
                    account['auth_method'] = 'ldap'
                    account['auth_server'] = info['ldap_server']
                    account['user_auth_name'] = info['user_ldap_name']
                    if not was_external:
                        info['details'] += 'Authentication is no longer internal.<br>'
    elif info['user_password'] and allow_internal:
        from register import _check_password
        info['user_password_check'] = req.form['user_password_check'].value
        (err, info['details']) = _check_password(info, err)
        if not err:
            account['auth_method'] = ''
            account['auth_server'] = account['user_auth_name'] = ''
            account['password'] = info['user_password']
            if was_external:
                info['details'] += 'Authentication is now internal.<br>'
    else:
        info['details'] += 'Note: no authentication changes made.<br>'
    if not err:
        try:
            manage_kbasix._account_mod(info['login_name'], 'profile', profile)
            manage_users._mod(info['login_name'], account)
        except Exception as reason:
            raise UpdateError(reason)
    if err:
        info['class'] = 'fail'
        info['status_button_1'] = aux._go_back_button(req, info['token'])
        info['status_button_2'] = ''
        logging.debug('Failed profile update because "%s" (%s)' % \
                          (info['details'].replace('<br>',' '), info['login_name']))
    else:
        info['class'] = 'success'
        info['details'] += aux._fill_str(info['successful_update_blurb'], info)
        if info['access'] == 'profile.py':
            info['status_button_1'] = """
          <form action="../login.py/process?start" method="post">
            <input type="submit" value="Login" />
          </form><br>
""" % info
        else:
            info['status_button_1'] = ''
        info['status_button_2'] = ''
        logging.debug('Successful profile update (%s)' % info['login_name'])
    info['title'] = 'User Profile'
    return aux._fill_page(info['status_page_'], info)
예제 #39
0
def _update(req, info):
    """Update the metadata.

       _update(req, info)

    Returns a status web page.
    """
    import logging
    import aux
    import cgi
    import file_manager
    info['file_tag'] = req.form['file_tag'].value
    logging.debug('Updating metadata of "%s" (%s)' % \
                      (info['file_tag'], info['login_name']))
    info['title'] = 'Metadata Editor'
    # This is a generic status button, we make the variable substitutions
    # below.
    info['status_button_1'] = """
    <form action="../metaeditor.py/process?start&file_tag=%(file_tag)s"
     method="post">
      <input type="hidden" name="token" value="%(token)s" />
      <input type="submit" value="Back" />
    </form>
    """
    info['status_button_2'] = ''
    file_manager._check_file_tag(info['file_tag'], info['login_name'])
    file_info = file_manager._get_file_info(info['file_tag'], info)
    if not file_info or file_info['owner_uid'] != info['uid']:
        raise UpdateError('Invalid file ownership "%s" (%s)' % \
                              (info['file_tag'], info['login_name']))
    info['details'] = ''
    form_dict = req.form
    if 'local_share' not in form_dict:
        form_dict['local_share'] = ''
    if 'world_share' not in form_dict:
        form_dict['world_share'] = ''
    # Single quotes are escaped (string is inside '')
    # Without .value: 'file_type': Field('file_type', 'Unknown')
    # With .value: 'file_type': 'Unknown'
    for key, rawval in form_dict.items():
        val = cgi.escape(rawval.value, True)
        if key in info['basic_meta'] or key in \
                ['local_share', 'world_share']:
            if key in ['uid_shares', 'gid_shares', 'local_share', \
                           'world_share']:
                err = ''
                try:
                    val.encode('ascii')
                except:
                    info['class'] = 'fail'
                    info['details'] += 'Shares must be ASCII-encoded'
                    info['status_button_1'] = info['status_button_1'] % info
                    logging.debug('Non-ASCII shares found (%s)' % \
                                      info['login_name'])
                    return aux._fill_page(info['status_page_'], info)
                logging.debug('Processing share "%s" with input value \
"%s" (%s)' % (key, val, info['login_name']))
                # Point shares are done with specific users and groups.
                # Wide shares are with all registered users or the world.
                if key in ['uid_shares', 'gid_shares']:
                    (file_info[key], err) = \
                        _set_point_shares(file_info[key], val, key, info)
                if key in ['local_share', 'world_share']:
                    file_info[key] = \
                        _set_wide_share(file_info[key], val, key, info)
                info['details'] += err
            else:
                file_info[key] = val
        elif key in file_info['custom']:
            file_info[key] = val
        elif key in ['token', 'file_tag', 'action']:
            pass
        else:
            try:
                key.encode('ascii')
            except:
                key = 'non-ASCII key'
            try:
                val.encode('ascii')
            except:
                val = 'non-ASCII value'
            logging.warn('Ignoring unknown key pair "%s: %s" (%s)' % \
                             (key, val, info['login_name']))
    _save_file_info(file_info, info['file_tag'], info)
    logging.debug('Successful metadata update (%s)' % info['login_name'])
    info['class'] = 'success'
    info['details'] += aux._fill_str(info['successful_update_blurb'], info)
    info['status_button_1'] = info['status_button_1'] % info
    return aux._fill_page(info['status_page_'], info)