Esempio n. 1
0
    def mark_as_teacher():
        if not is_admin(request) and not is_testing_request(request):
            return 'unauthorized', 403

        body = request.json

        # Validations
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('username'), str):
            return 'body.username must be a string', 400
        if not isinstance(body.get('is_teacher'), bool):
            return 'body.is_teacher must be boolean', 400

        user = DATABASE.user_by_username(body['username'].strip().lower())

        if not user:
            return 'invalid username', 400

        DATABASE.update_user(user['username'],
                             {'is_teacher': 1 if body['is_teacher'] else 0})

        if body['is_teacher'] and not is_testing_request(request):
            send_email_template('welcome_teacher', user['email'], '')

        return '', 200
Esempio n. 2
0
    def reset():
        body = request.json
        # Validations
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('username'), str):
            return 'body.username must be a string', 400
        if not isinstance(body.get('token'), str):
            return 'body.token must be a string', 400
        if not isinstance(body.get('password'), str):
            return 'body.password be a string', 400

        if len(body['password']) < 6:
            return 'password must be at least six characters long', 400

        # There's no need to trim or lowercase username, because it should come within a link prepared by the app itself and not inputted manually by the user.
        token = DATABASE.get_token(body['username'])
        if not token:
            return 'invalid username/token', 403
        if not check_password(body['token'], token['token']):
            return 'invalid username/token', 403

        hashed = hash(body['password'], make_salt())
        token = DATABASE.forget_token(body['username'])
        DATABASE.update_user(body['username'], {'password': hashed})
        user = DATABASE.user_by_username(body['username'])

        if not is_testing_request(request):
            send_email_template('reset_password', user['email'],
                                requested_lang(), None)

        return '', 200
Esempio n. 3
0
    def change_password(user):

        body = request.json
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('old_password'), str):
            return 'body.old_password must be a string', 400
        if not isinstance(body.get('new_password'), str):
            return 'body.new_password must be a string', 400

        if len(body['new_password']) < 6:
            return 'password must be at least six characters long', 400

        # The user object we got from 'requires_login' doesn't have the password, so look that up in the database
        user = DATABASE.user_by_username(user['username'])

        if not check_password(body['old_password'], user['password']):
            return 'invalid username/password', 403

        hashed = hash(body['new_password'], make_salt())

        DATABASE.update_user(user['username'], {'password': hashed})
        # We are not updating the user in the Flask session, because we should not rely on the password in anyway.
        if not is_testing_request(request):
            send_email_template('change_password', user['email'], None)

        return '', 200
Esempio n. 4
0
    def reset():
        body = request.json
        # Validations
        if not type_check(body, 'dict'):
            return 'body must be an object', 400
        if not object_check(body, 'username', 'str'):
            return 'body.username must be a string', 400
        if not object_check(body, 'token', 'str'):
            return 'body.token must be a string', 400
        if not object_check(body, 'password', 'str'):
            return 'body.password be a string', 400

        if len(body['password']) < 6:
            return 'password must be at least six characters long', 400

        # There's no need to trim or lowercase username, because it should come within a link prepared by the app itself and not inputted manually by the user.
        token = db_get('tokens', {'id': body['username']})
        if not token:
            return 'invalid username/token', 403
        if not check_password(body['token'], token['token']):
            return 'invalid username/token', 403

        hashed = hash(body['password'], make_salt())
        token = db_del('tokens', {'id': body['username']})
        db_update('users', {'username': body['username'], 'password': hashed})
        user = db_get('users', {'username': body['username']})

        if not is_testing_request(request):
            send_email_template('reset_password', user['email'],
                                requested_lang(), None)

        return '', 200
Esempio n. 5
0
File: app.py Progetto: Tazaria/hedy
def echo_session_vars_main():
    if not utils.is_testing_request(request):
        return 'This endpoint is only meant for E2E tests', 400
    return jsonify({
        'session': dict(session),
        'proxy_enabled': bool(os.getenv('PROXY_TO_TEST_HOST'))
    })
Esempio n. 6
0
def auth_templates(page, lang, menu, request):
    if page == 'my-profile':
        return render_template('profile.html',
                               lang=lang,
                               auth=TRANSLATIONS.get_translations(
                                   lang, 'Auth'),
                               menu=menu,
                               username=current_user(request)['username'],
                               is_teacher=is_teacher(request),
                               current_page='my-profile')
    if page in ['signup', 'login', 'recover', 'reset']:
        return render_template(page + '.html',
                               lang=lang,
                               auth=TRANSLATIONS.get_translations(
                                   lang, 'Auth'),
                               menu=menu,
                               username=current_user(request)['username'],
                               is_teacher=False,
                               current_page='login')
    if page == 'admin':
        if not is_testing_request(request) and not is_admin(request):
            return 'unauthorized', 403

        # After hitting 1k users, it'd be wise to add pagination.
        users = DATABASE.all_users()
        userdata = []
        fields = [
            'username', 'email', 'birth_year', 'country', 'gender', 'created',
            'last_login', 'verification_pending', 'is_teacher',
            'program_count', 'prog_experience', 'experience_languages'
        ]

        for user in users:
            data = {}
            for field in fields:
                if field in user:
                    data[field] = user[field]
                else:
                    data[field] = None
            data['email_verified'] = not bool(data['verification_pending'])
            data['is_teacher'] = bool(data['is_teacher'])
            data['created'] = mstoisostring(data['created'])
            if data['last_login']:
                data['last_login'] = mstoisostring(data['last_login'])
            userdata.append(data)

        userdata.sort(key=lambda user: user['created'], reverse=True)
        counter = 1
        for user in userdata:
            user['index'] = counter
            counter = counter + 1

        return render_template('admin.html',
                               lang=lang,
                               users=userdata,
                               program_count=DATABASE.all_programs_count(),
                               user_count=DATABASE.all_users_count(),
                               auth=TRANSLATIONS.get_translations(
                                   lang, 'Auth'))
Esempio n. 7
0
File: auth.py Progetto: moorlag/hedy
def update_is_teacher(user, is_teacher_value=1):
    user_is_teacher = 'is_teacher' in user and user['is_teacher']
    user_becomes_teacher = is_teacher_value and not user_is_teacher

    DATABASE.update_user(user['username'], {'is_teacher': is_teacher_value})

    if user_becomes_teacher and not is_testing_request(request):
        send_email_template('welcome_teacher', user['email'], '')
Esempio n. 8
0
    def get_class(user, class_id):
        if not is_teacher(request):
            return 'Only teachers can retrieve classes', 403
        Class = DATABASE.get_class(class_id)
        if not Class or Class['teacher'] != user['username']:
            return 'No such class', 404
        students = []
        for student_username in Class.get('students', []):
            student = DATABASE.user_by_username(student_username)
            programs = DATABASE.programs_for_user(student_username)
            highest_level = max(
                program['level']
                for program in programs) if len(programs) else 0
            sorted_public_programs = list(
                sorted(
                    [program for program in programs if program.get('public')],
                    key=lambda p: p['date']))
            if sorted_public_programs:
                latest_shared = sorted_public_programs[-1]
                latest_shared['link'] = os.getenv(
                    'BASE_URL') + f"/hedy/{latest_shared['id']}/view"
            else:
                latest_shared = None
            students.append({
                'username':
                student_username,
                'last_login':
                utils.mstoisostring(student['last_login']),
                'programs':
                len(programs),
                'highest_level':
                highest_level,
                'latest_shared':
                latest_shared
            })

        if utils.is_testing_request(request):
            return jsonify({
                'students': students,
                'link': Class['link'],
                'name': Class['name'],
                'id': Class['id']
            })
        return render_template(
            'class-overview.html',
            lang=requested_lang(),
            auth=TRANSLATIONS.get_translations(requested_lang(), 'Auth'),
            menu=render_main_menu('my-profile'),
            username=current_user(request)['username'],
            is_teacher=is_teacher(request),
            current_page='my-profile',
            class_info={
                'students': students,
                'link': os.getenv('BASE_URL') + '/hedy/l/' + Class['link'],
                'name': Class['name'],
                'id': Class['id']
            })
Esempio n. 9
0
def redirect_ab (request):
    # If this is a testing request, we return True
    if utils.is_testing_request (request):
        return True
    # If the user is logged in, we use their username as identifier, otherwise we use the session id
    user_identifier = current_user(request) ['username'] or str (session['session_id'])

    # This will send either % PROXY_TO_TEST_PROPORTION of the requests into redirect, or 50% if that variable is not specified.
    redirect_proportion = int (os.getenv ('PROXY_TO_TEST_PROPORTION', '50'))
    redirect_flag = (hash_user_or_session (user_identifier) % 100) < redirect_proportion
    return redirect_flag
Esempio n. 10
0
    def get_class(user, class_id):
        app.logger.info('This is info output')
        if not is_teacher(user):
            return utils.page_403(ui_message='retrieve_class')
        Class = DATABASE.get_class(class_id)
        if not Class or Class['teacher'] != user['username']:
            return utils.page_404(ui_message='no_such_class')
        students = []
        for student_username in Class.get('students', []):
            student = DATABASE.user_by_username(student_username)
            programs = DATABASE.programs_for_user(student_username)
            highest_level = max(
                program['level']
                for program in programs) if len(programs) else 0
            sorted_public_programs = list(
                sorted(
                    [program for program in programs if program.get('public')],
                    key=lambda p: p['date']))
            if sorted_public_programs:
                latest_shared = sorted_public_programs[-1]
                latest_shared['link'] = f"/hedy/{latest_shared['id']}/view"
            else:
                latest_shared = None
            students.append({
                'username':
                student_username,
                'last_login':
                utils.datetotimeordate(
                    utils.mstoisostring(student['last_login'])),
                'programs':
                len(programs),
                'highest_level':
                highest_level,
                'latest_shared':
                latest_shared
            })

        if utils.is_testing_request(request):
            return jsonify({
                'students': students,
                'link': Class['link'],
                'name': Class['name'],
                'id': Class['id']
            })
        return render_template(
            'class-overview.html',
            current_page='for-teachers',
            page_title=hedyweb.get_page_title('class overview'),
            class_info={
                'students': students,
                'link': '/hedy/l/' + Class['link'],
                'name': Class['name'],
                'id': Class['id']
            })
Esempio n. 11
0
def auth_templates(page, page_title, lang, request):
    if page == 'my-profile':
        return render_template('profile.html',
                               page_title=page_title,
                               current_page='my-profile')
    if page in ['signup', 'login', 'recover', 'reset']:
        return render_template(page + '.html',
                               page_title=page_title,
                               is_teacher=False,
                               current_page='login')
    if page == 'admin':
        if not is_testing_request(request) and not is_admin(current_user()):
            return 'unauthorized', 403

        # After hitting 1k users, it'd be wise to add pagination.
        users = DATABASE.all_users()
        userdata = []
        fields = [
            'username', 'email', 'birth_year', 'country', 'gender', 'created',
            'last_login', 'verification_pending', 'is_teacher',
            'program_count', 'prog_experience', 'experience_languages'
        ]

        for user in users:
            data = pick(user, *fields)
            data['email_verified'] = not bool(data['verification_pending'])
            data['is_teacher'] = bool(data['is_teacher'])
            data['created'] = mstoisostring(
                data['created']) if data['created'] else '?'
            if data['last_login']:
                data['last_login'] = mstoisostring(
                    data['last_login']) if data['last_login'] else '?'
            userdata.append(data)

        userdata.sort(key=lambda user: user['created'], reverse=True)
        counter = 1
        for user in userdata:
            user['index'] = counter
            counter = counter + 1

        return render_template('admin.html',
                               users=userdata,
                               page_title=page_title,
                               program_count=DATABASE.all_programs_count(),
                               user_count=DATABASE.all_users_count())
Esempio n. 12
0
    def change_user_email():
        if not is_admin(request):
            return 'unauthorized', 403

        body = request.json

        # Validations
        if not type_check(body, 'dict'):
            return 'body must be an object', 400
        if not object_check(body, 'username', 'str'):
            return 'body.username must be a string', 400
        if not object_check(body, 'email', 'str'):
            return 'body.email must be a string', 400
        if not valid_email(body['email']):
            return 'email must be a valid email', 400

        user = db_get('users', {'username': body['username'].strip().lower()})

        if not user:
            return 'invalid username', 400

        token = make_salt()
        hashed_token = hash(token, make_salt())

        # We assume that this email is not in use by any other users. In other words, we trust the admin to enter a valid, not yet used email address.
        db_update(
            'users', {
                'username': user['username'],
                'email': body['email'],
                'verification_pending': hashed_token
            })

        # If this is an e2e test, we return the email verification token directly instead of emailing it.
        if is_testing_request(request):
            resp = {'username': user['username'], 'token': hashed_token}
        else:
            send_email_template(
                'welcome_verify', body['email'], requested_lang(),
                os.getenv('BASE_URL') + '/auth/verify?username='******'username']) + '&token=' +
                urllib.parse.quote_plus(hashed_token))

        return '', 200
Esempio n. 13
0
    def change_user_email():
        user = current_user()
        if not is_admin(user):
            return 'unauthorized', 403

        body = request.json

        # Validations
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('username'), str):
            return 'body.username must be a string', 400
        if not isinstance(body.get('email'), str):
            return 'body.email must be a string', 400
        if not valid_email(body['email']):
            return 'email must be a valid email', 400

        user = DATABASE.user_by_username(body['username'].strip().lower())

        if not user:
            return 'invalid username', 400

        token = make_salt()
        hashed_token = hash(token, make_salt())

        # We assume that this email is not in use by any other users. In other words, we trust the admin to enter a valid, not yet used email address.
        DATABASE.update_user(user['username'], {
            'email': body['email'],
            'verification_pending': hashed_token
        })

        # If this is an e2e test, we return the email verification token directly instead of emailing it.
        if is_testing_request(request):
            resp = {'username': user['username'], 'token': hashed_token}
        else:
            send_email_template(
                'welcome_verify', body['email'],
                email_base_url() + '/auth/verify?username='******'username']) + '&token=' +
                urllib.parse.quote_plus(hashed_token))

        return '', 200
Esempio n. 14
0
    def recover():
        body = request.json
        # Validations
        if not type_check(body, 'dict'):
            return 'body must be an object', 400
        if not object_check(body, 'username', 'str'):
            return 'body.username must be a string', 400

        # If username has an @-sign, then it's an email
        if '@' in body['username']:
            user = db_get('users', {'email': body['username'].strip().lower()},
                          True)
        else:
            user = db_get('users',
                          {'username': body['username'].strip().lower()})

        if not user:
            return 'invalid username', 403

        token = make_salt()
        hashed = hash(token, make_salt())

        db_create(
            'tokens', {
                'id': user['username'],
                'token': hashed,
                'ttl': times() + session_length
            })

        if is_testing_request(request):
            # If this is an e2e test, we return the email verification token directly instead of emailing it.
            return jsonify({'username': user['username'], 'token': token}), 200
        else:
            send_email_template(
                'recover_password', user['email'], requested_lang(),
                os.getenv('BASE_URL') + '/reset?username='******'username']) + '&token=' +
                urllib.parse.quote_plus(token))
            return '', 200
Esempio n. 15
0
File: auth.py Progetto: moorlag/hedy
    def mark_as_teacher():
        if not is_admin(request) and not is_testing_request(request):
            return 'unauthorized', 403

        body = request.json

        # Validations
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('username'), str):
            return 'body.username must be a string', 400
        if not isinstance(body.get('is_teacher'), bool):
            return 'body.is_teacher must be boolean', 400

        user = DATABASE.user_by_username(body['username'].strip().lower())

        if not user:
            return 'invalid username', 400

        is_teacher_value = 1 if body['is_teacher'] else 0
        update_is_teacher(user, is_teacher_value)

        return '', 200
Esempio n. 16
0
    def change_password(user):

        body = request.json
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('old_password'), str):
            return 'body.old_password must be a string', 400
        if not isinstance(body.get('new_password'), str):
            return 'body.new_password must be a string', 400

        if len(body['new_password']) < 6:
            return 'password must be at least six characters long', 400

        if not check_password(body['old_password'], user['password']):
            return 'invalid username/password', 403

        hashed = hash(body['new_password'], make_salt())

        DATABASE.update_user(user['username'], {'password': hashed})
        if not is_testing_request(request):
            send_email_template('change_password', user['email'], None)

        return '', 200
Esempio n. 17
0
    def change_password(user):

        body = request.json
        if not type_check(body, 'dict'):
            return 'body must be an object', 400
        if not object_check(body, 'old_password', 'str'):
            return 'body.old_password must be a string', 400
        if not object_check(body, 'new_password', 'str'):
            return 'body.new_password must be a string', 400

        if len(body['new_password']) < 6:
            return 'password must be at least six characters long', 400

        if not check_password(body['old_password'], user['password']):
            return 'invalid username/password', 403

        hashed = hash(body['new_password'], make_salt())

        db_update('users', {'username': user['username'], 'password': hashed})
        if not is_testing_request(request):
            send_email_template('change_password', user['email'],
                                requested_lang(), None)

        return '', 200
Esempio n. 18
0
    def recover():
        body = request.json
        # Validations
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if not isinstance(body.get('username'), str):
            return 'body.username must be a string', 400

        # If username has an @-sign, then it's an email
        if '@' in body['username']:
            user = DATABASE.user_by_email(body['username'].strip().lower())
        else:
            user = DATABASE.user_by_username(body['username'].strip().lower())

        if not user:
            return 'invalid username', 403

        token = make_salt()
        hashed = hash(token, make_salt())

        DATABASE.store_token({
            'id': user['username'],
            'token': hashed,
            'ttl': times() + session_length
        })

        if is_testing_request(request):
            # If this is an e2e test, we return the email verification token directly instead of emailing it.
            return jsonify({'username': user['username'], 'token': token}), 200
        else:
            send_email_template(
                'recover_password', user['email'],
                os.getenv('BASE_URL') + '/reset?username='******'username']) + '&token=' +
                urllib.parse.quote_plus(token))
            return '', 200
Esempio n. 19
0
    def signup():
        body = request.json
        # Validations, mandatory fields
        if not type_check(body, 'dict'):
            return 'body must be an object', 400
        if not object_check(body, 'username', 'str'):
            return 'username must be a string', 400
        if '@' in body['username']:
            return 'username cannot contain an @-sign', 400
        if ':' in body['username']:
            return 'username cannot contain a colon', 400
        if len(body['username'].strip()) < 3:
            return 'username must be at least three characters long', 400
        if not object_check(body, 'password', 'str'):
            return 'password must be a string', 400
        if len(body['password']) < 6:
            return 'password must be at least six characters long', 400
        if not object_check(body, 'email', 'str'):
            return 'email must be a string', 400
        if not valid_email(body['email']):
            return 'email must be a valid email', 400
        # Validations, optional fields
        if 'country' in body:
            if not body['country'] in countries:
                return 'country must be a valid country', 400
        if 'birth_year' in body:
            if not object_check(
                    body, 'birth_year',
                    'int') or body['birth_year'] <= 1900 or body[
                        'birth_year'] > datetime.datetime.now().year:
                return 'birth_year must be a year between 1900 and ' + datetime.datetime.now(
                ).year, 400
        if 'gender' in body:
            if body['gender'] != 'm' and body['gender'] != 'f' and body[
                    'gender'] != 'o':
                return 'gender must be m/f/o', 400

        user = db_get('users', {'username': body['username'].strip().lower()})
        if user:
            return 'username exists', 403
        email = db_get('users', {'email': body['email'].strip().lower()}, True)
        if email:
            return 'email exists', 403

        hashed = hash(body['password'], make_salt())

        token = make_salt()
        hashed_token = hash(token, make_salt())
        username = body['username'].strip().lower()
        email = body['email'].strip().lower()

        if not is_testing_request(
                request) and 'subscribe' in body and body['subscribe'] == True:
            # If we have a Mailchimp API key, we use it to add the subscriber through the API
            if os.getenv('MAILCHIMP_API_KEY') and os.getenv(
                    'MAILCHIMP_AUDIENCE_ID'):
                # The first domain in the path is the server name, which is contained in the Mailchimp API key
                request_path = 'https://' + os.getenv(
                    'MAILCHIMP_API_KEY').split(
                        '-')[1] + '.api.mailchimp.com/3.0/lists/' + os.getenv(
                            'MAILCHIMP_AUDIENCE_ID') + '/members'
                request_headers = {
                    'Content-Type': 'application/json',
                    'Authorization': 'apikey ' + os.getenv('MAILCHIMP_API_KEY')
                }
                request_body = {'email_address': email, 'status': 'subscribed'}
                r = requests.post(request_path,
                                  headers=request_headers,
                                  data=json.dumps(request_body))

                subscription_error = None
                if r.status_code != 200 and r.status_code != 400:
                    subscription_error = True
                # We can get a 400 if the email is already subscribed to the list. We should ignore this error.
                if r.status_code == 400 and not re.match(
                        '.*already a list member', r.text):
                    subscription_error = True
                # If there's an error in subscription through the API, we report it to the main email address
                if subscription_error:
                    send_email(
                        config['email']['sender'],
                        'ERROR - Subscription to Hedy newsletter on signup',
                        email, '<p>' + email + '</p><pre>Status:' +
                        str(r.status_code) + '    Body:' + r.text + '</pre>')
            # Otherwise, we send an email to notify about this to the main email address
            else:
                send_email(config['email']['sender'],
                           'Subscription to Hedy newsletter on signup', email,
                           '<p>' + email + '</p>')

        user = {
            'username': username,
            'password': hashed,
            'email': email,
            'created': timems(),
            'verification_pending': hashed_token,
            'last_login': timems()
        }

        if 'country' in body:
            user['country'] = body['country']
        if 'birth_year' in body:
            user['birth_year'] = body['birth_year']
        if 'gender' in body:
            user['gender'] = body['gender']

        db_create('users', user)

        # We automatically login the user
        cookie = make_salt()
        db_create(
            'tokens', {
                'id': cookie,
                'username': user['username'],
                'ttl': times() + session_length
            })

        # If this is an e2e test, we return the email verification token directly instead of emailing it.
        if is_testing_request(request):
            resp = make_response({'username': username, 'token': hashed_token})
        # Otherwise, we send an email with a verification link and we return an empty body
        else:
            send_email_template(
                'welcome_verify', email, requested_lang(),
                os.getenv('BASE_URL') + '/auth/verify?username='******'&token=' +
                urllib.parse.quote_plus(hashed_token))
            resp = make_response({})

        # We set the cookie to expire in a year, just so that the browser won't invalidate it if the same cookie gets renewed by constant use.
        # The server will decide whether the cookie expires.
        resp.set_cookie(cookie_name,
                        value=cookie,
                        httponly=True,
                        secure=True,
                        samesite='Lax',
                        path='/',
                        max_age=365 * 24 * 60 * 60)
        return resp
Esempio n. 20
0
File: app.py Progetto: Tazaria/hedy
def echo_session_vars_test():
    if not utils.is_testing_request(request):
        return 'This endpoint is only meant for E2E tests', 400
    return jsonify({'session': dict(session)})
Esempio n. 21
0
File: app.py Progetto: Tazaria/hedy
def before_request_proxy_testing():
    if utils.is_testing_request(request):
        if os.getenv('IS_TEST_ENV'):
            session['test_session'] = 'test'
Esempio n. 22
0
File: app.py Progetto: Tazaria/hedy
 def reject_e2e_requests():
     if utils.is_testing_request(request):
         return 'No E2E tests are allowed in production', 400
Esempio n. 23
0
    def update_profile(user):

        body = request.json
        if not type_check(body, 'dict'):
            return 'body must be an object', 400
        if 'email' in body:
            if not object_check(body, 'email', 'str'):
                return 'body.email must be a string', 400
            if not valid_email(body['email']):
                return 'body.email must be a valid email', 400
        if 'country' in body:
            if not body['country'] in countries:
                return 'body.country must be a valid country', 400
        if 'birth_year' in body:
            if not object_check(
                    body, 'birth_year',
                    'int') or body['birth_year'] <= 1900 or body[
                        'birth_year'] > datetime.datetime.now().year:
                return 'birth_year must be a year between 1900 and ' + str(
                    datetime.datetime.now().year), 400
        if 'gender' in body:
            if body['gender'] != 'm' and body['gender'] != 'f' and body[
                    'gender'] != 'o':
                return 'body.gender must be m/f/o', 400

        resp = {}
        if 'email' in body:
            email = body['email'].strip().lower()
            if email != user['email']:
                exists = db_get('users', {'email': email}, True)
                if exists:
                    return 'email exists', 403
                token = make_salt()
                hashed_token = hash(token, make_salt())
                db_update(
                    'users', {
                        'username': user['username'],
                        'email': email,
                        'verification_pending': hashed_token
                    })
                # If this is an e2e test, we return the email verification token directly instead of emailing it.
                if is_testing_request(request):
                    resp = {
                        'username': user['username'],
                        'token': hashed_token
                    }
                else:
                    send_email_template(
                        'welcome_verify', email, requested_lang(),
                        os.getenv('BASE_URL') + '/auth/verify?username='******'username']) + '&token=' +
                        urllib.parse.quote_plus(hashed_token))

        if 'country' in body:
            db_update('users', {
                'username': user['username'],
                'country': body['country']
            })
        if 'birth_year' in body:
            db_update('users', {
                'username': user['username'],
                'birth_year': body['birth_year']
            })
        if 'gender' in body:
            db_update('users', {
                'username': user['username'],
                'gender': body['gender']
            })

        return jsonify(resp)
Esempio n. 24
0
    def update_profile(user):

        body = request.json
        if not isinstance(body, dict):
            return 'body must be an object', 400
        if 'email' in body:
            if not isinstance(body.get('email'), str):
                return 'body.email must be a string', 400
            if not valid_email(body['email']):
                return 'body.email must be a valid email', 400
        if 'country' in body:
            if not body['country'] in countries:
                return 'body.country must be a valid country', 400
        if 'birth_year' in body:
            if not isinstance(body.get('birth_year'),
                              int) or body['birth_year'] <= 1900 or body[
                                  'birth_year'] > datetime.datetime.now().year:
                return 'birth_year must be a year between 1900 and ' + str(
                    datetime.datetime.now().year), 400
        if 'gender' in body:
            if body['gender'] != 'm' and body['gender'] != 'f' and body[
                    'gender'] != 'o':
                return 'body.gender must be m/f/o', 400
        if 'prog_experience' in body and body['prog_experience'] not in [
                'yes', 'no'
        ]:
            return 'If present, prog_experience must be "yes" or "no"', 400
        if 'experience_languages' in body:
            if not isinstance(body['experience_languages'], list):
                return 'If present, experience_languages must be an array', 400
            for language in body['experience_languages']:
                if language not in [
                        'scratch', 'other_block', 'python', 'other_text'
                ]:
                    return 'Invalid language: ' + str(language), 400

        resp = {}
        if 'email' in body:
            email = body['email'].strip().lower()
            if email != user['email']:
                exists = DATABASE.user_by_email(email)
                if exists:
                    return 'email exists', 403
                token = make_salt()
                hashed_token = hash(token, make_salt())
                DATABASE.update_user(user['username'], {
                    'email': email,
                    'verification_pending': hashed_token
                })
                # If this is an e2e test, we return the email verification token directly instead of emailing it.
                if is_testing_request(request):
                    resp = {
                        'username': user['username'],
                        'token': hashed_token
                    }
                else:
                    send_email_template(
                        'welcome_verify', email, requested_lang(),
                        os.getenv('BASE_URL') + '/auth/verify?username='******'username']) + '&token=' +
                        urllib.parse.quote_plus(hashed_token))

        username = user['username']

        updates = {}
        for field in [
                'country', 'birth_year', 'gender', 'prog_experience',
                'experience_languages'
        ]:
            if field in body:
                if field == 'experience_languages' and len(body[field]) == 0:
                    updates[field] = None
                else:
                    updates[field] = body[field]

        if updates:
            DATABASE.update_user(username, updates)

        return jsonify(resp)